前后端代码:GitHub地址
Vue-Cli创建前端项目

前端项目目录结构
我们需要Login.vue和Home.vue组件,一个用于登录,一个用于登录成功之后的跳转。
我们需要封装一些函数,这些函数用于在前后端交互时请求与响应的拦截。定义api.js
我们还需要删除原有的组件,清除App.vue的内容(不能删除)。

引入Element-ui
参考element-ui官网
在main.js文件中引入Element-ui
1 2 3 4
| import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
|
在VScode终端输入npm i element-ui -S

在main.js中引入element-ui
1 2 3 4
| import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
|
可以在package.json中查看是否引入成功
1 2 3 4 5
| "dependencies": { "element-ui": "^2.15.1", "vue": "^2.5.2", "vue-router": "^3.0.1" },
|
代码实现:
Login.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| <template> <div> <el-form ref="loginform" :rules="rules" :model="loginForm" class="loginContainer"> <h3 class="loginTitle">系统登录</h3> <el-form-item prop="username"> <!-- auto-complete="off" 禁止浏览器表单自动填充 --> <!-- placeholder 输入框占位文本 --> <el-input type="text" auto-complete="off" v-model="loginForm.username" placeholder="请输入用户名"></el-input> </el-form-item>
<el-form-item prop="password"> <el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" ></el-input> </el-form-item>
<el-form-item prop="code"> <el-input type="text" v-model="loginForm.code" size="normal" placeholder="点击图片更换验证码" auto-complete="off" style="width:250px;margin-right:5px"></el-input> <img :src="captchaUrl" @click="updataCaptcha"> </el-form-item>
<el-button type="primary" style="width:100%" @click="submitLogin">登录</el-button>
</el-form> </div> </template>
<script> import {postRequest} from "../utils/api"; export default { name:"Login", data(){ return{ captchaUrl:'captcha?time' + new Date(), loginForm:{ username:'', password:'', code:'' }, // rules 表单的验证 required message:提示信息 rules:{ username:[{required:true,message:'请输入用户名',trigger:'blur'}], password:[{required:true,message:'请输入密码',trigger:'blur'}], code:[{required:true,message:'请输入验证码',trigger:'blur'}] } } }, methods:{ //点击更新图片 updataCaptcha(){ this.captchaUrl = 'captcha?time=' + new Date(); }, //登录 submitLogin(){ this.$refs.loginForm.validate((valid) => { if(valid){ postRequest('/login',this.loginForm).then(resp => { console.log(resp); if(resp){ const token = resp.object.tokenHead + resp.object.token; window.sessionStorage.setItem('tokenStr',token) this.$router.replace('/Home') } }) }else{ console.log('error submit'); return false; } }) } } } </script>
<style> .loginContainer{ border-radius: 15px; background-clip: padding-box; margin: 180px auto; width: 350px; padding: 15px 35px 15px 35px; background:#fff; border: 1px solid #eaeaea; box-shadow: 0 0 25px #cac6c6; }
.loginTitle{ margin: 0px auto 40px auto; text-align: center; } </style>
|
Api.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| import axios from 'axios' import { Message } from 'element-ui' import router from '../router'
axios.interceptors.request.use(config => { if(window.sessionStorage.getItem('tokenStr')){ config.headers['Authorization'] = window.sessionStorage.getItem('tokenStr'); } return config },error => { console.log(error); })
axios.interceptors.response.use(success =>{ if(success.status && success.status == 200){ if(success.data.code == 500 || success.data.code == 401 || success.data.code == 403){ Message.error({message:success.data.message}); return; }
if(success.data.message){ Message.success({message:success.data.message}) } } return success.data; },error=>{ if(error.response.code == 504 || error.response.code == 404){ Message.error({message:'服务器崩了'}); }else if(error.response.code == 403){ Message.error({message:'权限不足'}) }else if(error.response.code == 401){ Message.error({message:'未登录'}) router.replace('/') }else{ if(error.response.data.message){ Message.error({message:error.response.data.message}) }else{ Message.error({message:'未知错误'}) } } })
let base = '';
export const postRequest = (url,params) => { return axios({ method:'post', url:`${base}${url}`, data:params }) }
|
跨域
在index.js中
1 2 3 4 5 6 7 8 9 10 11 12 13
| proxyTable: { '/ws':{ ws:true, target: 'ws://localhost:8081' }, '/':{ ws: false, target: 'http://localhost:8081', changeOrigin: true, pathRewrite: { '^/': '/' } }
|
后端:
Config
CaptchaConfig: 验证码配置文件
JwtTokenFilter:JWT过滤器,判断受否拿到JWT,判断JWT是否有效
SecurityConfig:SpringSecurity 核心配置文件
Controller
CaptchaController:生成验证码
LoginController:登录
dao
AdminMapper:数据库查询
pojo
Admin:实现 UserDetails接口
LoginAdmin:用于接收前端传来的信息,属性有:用户名,密码,验证码
service
AdminServiceImpl:用于登录的主要逻辑
utils
JWTUtils:生成JWT
Respbean:向前端返回结果类
项目框架

引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.2.0</version> </dependency>
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
<dependency> <groupId>com.github.axet</groupId> <artifactId>kaptcha</artifactId> <version>0.0.9</version> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency>
</dependencies>
|
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/login spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=root
mybatis.mapper-locations=classpath*:/Mappers/*.xml
jwt-tokenHeader=Authorization
jwt-secret=yeb-secret
jwt-expiration=604800
jwt-tokenHead=Bearer
|
LoginController
1 2 3 4 5 6 7 8 9 10 11 12 13
| @RestController public class LoginController {
@Autowired private AdminServiceImpl adminService;
@PostMapping("/login") public RespBean login(@RequestBody LoginAdmin loginAdmin, HttpServletRequest request){ return adminService.login(loginAdmin.getUsername(),loginAdmin.getPassword(),loginAdmin.getCode(),request); } }
|
LoginServiceImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| @Service public class AdminServiceImpl implements AdminService {
@Autowired private AdminMapper adminMapper;
@Autowired private UserDetailsService userDetailsService;
@Autowired private PasswordEncoder passwordEncoder;
@Autowired private JWTUtils jwtUtils;
@Value("${jwt-tokenHead}") private String tokenHead;
@Override public Admin selectAdminByName(String name) { return adminMapper.selectOne(name); }
@Override public RespBean login(String username, String password, String code, HttpServletRequest request) { String captcha = (String)request.getSession().getAttribute("captcha"); System.out.println(captcha); if(StringUtils.isEmpty(code) || !captcha.equalsIgnoreCase(code)){ return RespBean.error("验证码错误,请重新输入"); }
UserDetails userDetails = userDetailsService.loadUserByUsername(username); System.out.println(userDetails);
if(null == userDetails || !passwordEncoder.matches(password,userDetails.getPassword())){ return RespBean.error("用户名或密码错误"); }
if(!userDetails.isEnabled()){ return RespBean.error("账号被禁用,请联系管理员"); }
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authenticationToken); String token = jwtUtils.generateToken(userDetails);
Map<String, String> map = new HashMap<>(); map.put("token",token); map.put("tokenHead",tokenHead); return RespBean.success("登录成功",map); } }
|
SecurityConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private AdminServiceImpl adminService;
@Override @Bean protected UserDetailsService userDetailsService() { return username -> { Admin admin = adminService.selectAdminByName(username);
if(null != admin){ return admin; }
throw new UsernameNotFoundException("用户名或密码不正确"); }; }
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); }
@Bean public JwtTokenFilter jwtTokenFilter(){ return new JwtTokenFilter(); }
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf() .disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/login","/logout") .permitAll() .anyRequest() .authenticated() .and() .headers() .cacheControl();
http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } }
|