vue登陆拦截初体验
也是第一次做vue前后端分离 用户注册 登录拦截。项目已经搭建完成,下面分享一下搭建过程。
前端vue
1.login界面
2.注册页面
3.展示页面
前台页面在element官网组件有详细教程,这里不再赘诉。主要还是后台逻辑的实现不是嘛,哈哈。
逻辑分析
1.登陆
前台vue将user对象传到controller层,在service里处理相关业务逻辑:
public ServerResponse login(User user , HttpSession session) {
//通过 用户名获取数据库用户
User userByName = userMapper.findUserByName(user.getName());
if(userByName==null){//无法通过用户名获取数据库用户,即用户不存在
return ServerResponse.error("用户不存在");
}
//通过数据库用户,获得salt,然后将前台的user的密码加salt,然后md5两次加密后和数据库的密码匹配
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + userByName.getSalt()));
if(!pwd.equals(userByName.getPassword())){//密码匹配失败
return ServerResponse.error("密码错误");
}
//用户名,密码验证成功,放入session中
session.setAttribute(SystemConstant.SESSION_KEY,userByName);
return ServerResponse.success();
}
2.注册
注册要验证用户名是否已存在,而且两次密码要相同,那么很舒服的是element官网提供了我们验证表单的代码,要学会粘贴修改使用它即可。
当填写用户名是,有一个blur失去焦点事件,当触发该事件时要调用后台的根据名称查询用户的方法。
public ServerResponse findUserByName(String name) {
User user = userMapper.findUserByName(name);
if(user == null){
return ServerResponse.success();
}
return ServerResponse.error();
}
当用户名,密码有问题时,表单不允许提交。然后将用户名和验证好的密码传入后台。
public ServerResponse register(User user) {
//为确保密码安全性,给密码加盐
String salt = RandomStringUtils.randomAlphanumeric(20);
//为确保密码安全性,两次MD5加密
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + salt));
user.setCreateDate(new Date());
//将盐保存数据库,只有对应的密码和对应的盐在一起才会生成正确的密码
user.setSalt(salt);
user.setPassword(pwd);
//新增用户
userMapper.register(user);
return ServerResponse.success();
}
现有的坑
1.前后端分离,跨域问题?
2.前后端分离,cookies无法传到后台?
3.后端返回对应状态码时,前端怎么把对应的状态码转换跳转到对应的页面?
4.登录拦截怎么实现?
1.前后端分离,跨域问题?
通过后台的拦截器方法,每当请求controller层方法时,都要走拦截器类,
在拦截器类解决跨域问题:
//跨域
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"http://localhost:9090");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"x-auth,content-type,mtoken");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"PUT,POST,DELETE,GET");
response.addHeader("Access-Control-Allow-credentials","true");
2.前后端分离,cookies无法传到后台?
z在前台vue项目中 main.js 配置
axios.defaults.withCredentials = true
3.后端返回对应状态码时,前端怎么把对应的状态码转换跳转到对应的页面?
z在前台vue项目中 main.js 配置 前台拦截器:
对比状态码,通过location跳转指定页面。
4.登录拦截怎么实现?
首先我们要知道,有些方法是不能拦截的。所有要配置白名单,之前在登陆拦截的csdn里已经介绍过一种方法,现在介绍另一种方法。
我自定义一个注解,
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
}
通过该注解可以选择性过滤一些方法不拦截。
具体实现如下:
HandlerMethod handlerMethod= (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(Ignore.class)){
return true;
}
只要在我写过的方法上加上ignore了,方法就可以通通return true;
当我的session中没有user时就意味着登陆失败,那就要抛出一个我自定义的异常,返回给前台一个code,通过判断该code跳转页面。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//跨域
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"http://localhost:9090");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,"x-auth,content-type,mtoken");
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,"PUT,POST,DELETE,GET");
response.addHeader("Access-Control-Allow-credentials","true");
//拦截器白名单
HandlerMethod handlerMethod= (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(Ignore.class)){
return true;
}
User user = (User) request.getSession().getAttribute(SystemConstant.SESSION_KEY);
if(user != null){
return true;
}else{
throw new LoginException();
}
}
LoginException如下:
全局异常捕获。
全部代码
1.登陆vue:
<template>
<div style="margin-left: 35%; margin-top: 10%">
<el-form ref="user" :model="user" label-width="80px">
<el-form-item label="用户名" style="width: 260px">
<el-input v-model="user.name"></el-input>
</el-form-item>
<el-form-item label="密码" style="width: 260px">
<el-input type="password" v-model="user.password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="onSubmit" style="margin-left: 20px; margin-top: 20px">登陆</el-button>
<el-link type="primary" style="margin-left: 20px;" href="/register">注册</el-link>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "login",
data() {
return {
user: {
name: '123',
password: '123'
}
}
},
methods: {
onSubmit() {
var self = this;
this.$axios.post("http://localhost:8085/user/login.do",this.$qs.stringify(this.user)).then(function(res){
if(res.data.code==200){
self.$router.push('/product');
alert("登陆成功");
}else{
alert(res.data.data);
}
})
}
}
}
</script>
<style scoped>
</style>
2.注册
<template>
<div style="margin-left: 35%; margin-top: 10%">
<el-form :model="user" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用户名" prop="name" style="width:300px">
<el-input v-model.number="user.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" style="width: 300px">
<el-input type="password" v-model="user.password" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass" style="width: 300px">
<el-input type="password" v-model="user.checkPass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "register",
data() {
var checkName = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入用户名'));
} else {
if (this.user.name !== '') {
var self = this;
this.$axios.post("http://localhost:8085/user/findUserByName.do",this.$qs.stringify({"name":this.user.name})).then(function(res){
if(res.data.code==200){
callback();
}else{
callback(new Error('用户名已存在'));
}
})
}
}
};
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.user.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.user.password) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
user: {
password: '',
checkPass: '',
name: ''
},
rules: {
pass: [
{ validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ validator: validatePass2, trigger: 'blur' }
],
name: [
{ validator: checkName, trigger: 'blur' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
var self = this;
this.$axios.post("http://localhost:8085/user/register.do",this.$qs.stringify(this.user)).then(function(res){
if(res.data.code==200){
self.$router.push('/');
alert("注册成功");
}else{
alert("注册失败");
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
</style>
3.service层
package com.fh.service.impl;
import com.fh.common.ServerResponse;
import com.fh.mapper.UserMapper;
import com.fh.model.User;
import com.fh.service.UserService;
import com.fh.util.MD5Util;
import com.fh.util.SystemConstant;
import com.qcloud.cos.utils.Md5Utils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public ServerResponse findUserByName(String name) {
User user = userMapper.findUserByName(name);
if(user == null){
return ServerResponse.success();
}
return ServerResponse.error();
}
public ServerResponse register(User user) {
//为确保密码安全性,给密码加盐
String salt = RandomStringUtils.randomAlphanumeric(20);
//为确保密码安全性,两次MD5加密
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + salt));
user.setCreateDate(new Date());
//将盐保存数据库,只有对应的密码和对应的盐在一起才会生成正确的密码
user.setSalt(salt);
user.setPassword(pwd);
//新增用户
userMapper.register(user);
return ServerResponse.success();
}
public ServerResponse login(User user , HttpSession session) {
//通过 用户名获取数据库用户
User userByName = userMapper.findUserByName(user.getName());
if(userByName==null){//无法通过用户名获取数据库用户,即用户不存在
return ServerResponse.error("用户不存在");
}
//通过数据库用户,获得salt,然后将前台的user的密码加salt,然后md5两次加密后和数据库的密码匹配
String pwd = MD5Util.getPwd(MD5Util.getPwd(user.getPassword() + userByName.getSalt()));
if(!pwd.equals(userByName.getPassword())){//密码匹配失败
return ServerResponse.error("密码错误");
}
//用户名,密码验证成功,放入session中
session.setAttribute(SystemConstant.SESSION_KEY,userByName);
return ServerResponse.success();
}
}
4.全局异常处理
package com.fh.common;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(MyException.class)
public ServerResponse HandlerMyException (){
return ServerResponse.error();
}
@ExceptionHandler(LoginException.class)
public ServerResponse HandlerMyLoginException (){
return ServerResponse.error_login();
}
@ExceptionHandler(Exception.class)
public ServerResponse HandlerException (){
return ServerResponse.error();
}
}
5.自定义注解
package com.fh.common;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
}