登录权限验证

权限系统 为保证登录有效要做前后台的权限校验

写登录接口时 登陆成功向session中传递一个加密的AccessToken里面装着登录信息以及过期时间

  • 先写一个AccessToken类来装信息
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class AccessToken {
	//登录信息仅用name代替
    private String name;
	//过期时间
    private long expire;

    public AccessToken(String name){
        this.name = name;
        this.expire = System.currentTimeMillis() + 1000L * 60 * 2;
    }
}

加密解密工具类

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;

public class EncryptorUtil {
    private static StandardPBEStringEncryptor encryptor;
    static {
        encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword("et2004");
    }
    public static  String encrypt(String text){
        return encryptor.encrypt(text);
    }
    public static String decrypt(String ciphertext){
        return encryptor.decrypt(ciphertext);
    }
}

统一响应的返回格式

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultVo<T> {
    private int code;
    private String msg;
    private T data;

    private static final int SUCCESS_CODE=200;
    private static final int ERROR_CODE=201;
    private static final int NO_AUTH_CODE = 999;
    private static final String SUCCESS_MSG="SUCCESS";
    //成功!
    public static <T> ResultVo<T> success(T data){
        return new ResultVo<>(SUCCESS_CODE,SUCCESS_MSG,data);
    }

    public static ResultVo failed(String message) {
        return new ResultVo<>(ERROR_CODE,message,"");
    }
    public static ResultVo noAuth(String message) {
        return new ResultVo(NO_AUTH_CODE,message,"");
    }
}

登录接口:

/**
     * 登录接口
     * 1.根据用户名查询用户
     *      如果用户为空 则返回用户名或密码错误
     * 2.用户名不为空就验证密码
     *      密码不匹配 则返回用户名或密码错误
     * 3.验证通过
     *      创建AccessToken
     *      把AccessToken转成json字符串
     *      加密AccessToken的json字符串返回前端
     * */
    @PostMapping("/login")
    public ResultVo login(@RequestParam String name,@RequestParam String password){
        //
        User user = userService.getUser(name);
        if(ObjectUtils.isEmpty(user)){
            return ResultVo.failed("用户名或密码错误");
        }
        //验证用户密码
        password = DigestUtils.md5Hex(password);
        if(!StringUtils.equals(password,user.getPassword())){
            return ResultVo.failed("用户名或密码错误");
        }
        AccessToken token = new AccessToken(name);
        String jsonToken = JSONObject.toJSONString(token);
        return ResultVo.success(EncryptorUtil.encrypt(jsonToken));
    }

前端接到AccessToken后将其存到sessionStorage中

let qs = new URLSearchParams();
qs.append("name", this.loginForm.name);
 qs.append("password", this.loginForm.password);
 this.$http
   .post("/user/login",qs)
   .then((res) => {
     if (res.data.code === 200) {
       window.sessionStorage.setItem("token", res.data.data);
       this.$router.push("/");
     } else {
       this.$message.error(res.data.msg);
     }
   })
   .catch((e) => {
     console.log(e);
     this.$message.error("网络异常");
   });

自此前端的请求都要加上一个请求头token 在main.js中添加请求拦截器
返回的响应我们也要拿到响应中的token装到session中 添加响应拦截器
未登录时我们不能从导航栏跳转路由 设置全局路由守卫

import Vue from 'vue'

import App from './App.vue'
import router from './router'
import './assets/css/et2004.css'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);
//全局前置导航守卫
router.beforeEach((to,from,next)=>{
  //to:即将进入的路由
  //from:即将离开的路由
  //如果访问的是登录 直接放行
  if(to.path === '/login'){
    return next()
  }
  //验证是否有登录的token
  let token = window.sessionStorage.getItem('token')
  if(token){
    return next()
  }else{
    ElementUI.Message.error("登录失效,请重新登录")
    return next('/login')
  }
})
//请求拦截器
axios.interceptors.request.use(config =>{
  let url = config.url
  if(url === '/user/login'){
    return config
  }
  let token =window.sessionStorage.getItem('token')
  if(token){
    config.headers.token = token
    return config
  }else{
    router.push('/login')
    Promise.reject('用户未登录')
  }
  //标识放行
  return config
},error=>{
  Promise.reject(error)
})
//响应拦截器
axios.interceptors.response.use(response=>{
  //1.如果登录接口的响应 直接放行
  if(response.config.url === "/user/login"){
    return response
  }
  //2.判断返回码是否999.999表示无权限访问
  if(response.data.code === 999){
    console.log('认证失败:',response.data.msg);
    router.push('/login')
    return Promise.reject(error)
  }
  //拿到后端响应的token,重新设置到sessionStorage中
  console.log();
  let newToken = response.headers.token
  window.sessionStorage.setItem('token',newToken)
  return response
},error=>{
  Promise.request(error)
})

import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:8000/car-app'
Vue.prototype.$http = axios
Vue.prototype.contextPath = 'http://localhost:8000/car-app'
Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

而且在我们的后端也需要对所有除了login以外的请求携带的token做验证处理 这需要请求拦截器
LoginInterceptor
拦截器需要在配置类中配置

//登录拦截器
@Component
@Slf4j
public class LoginInterseptor implements HandlerInterceptor {

    private static final String OPTIONS_METHOD = "OPTIONS";
    private static final String TOKEN = "token";
    @Autowired
    UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.放行OPTION预检请求
        String method = request.getMethod();
        if (StringUtils.equals(OPTIONS_METHOD,method.toUpperCase())){
            log.info("options请求");
            return true;
        }
        //2.验证请求头中是否有token
        String tokenHeader = request.getHeader(TOKEN);
        if(StringUtils.isEmpty(tokenHeader)){
            this.noAuth(response,"没有携带认证数据");
            return false;
        }
        //3.解码token(解码之后是json 将json转换成AccessToken),验证有效期
        try{
            String jsonToken = EncryptorUtil.decrypt(tokenHeader);
            AccessToken accessToken = JSONObject.parseObject(jsonToken, AccessToken.class);
            //3.1 验证有效
            System.out.println(new Date().getTime()+"-"+accessToken.getExpire());
            if(new Date().getTime()-accessToken.getExpire()>=0){
                log.warn("token过期");
                this.noAuth(response,"token过期");
                return false;
            }
            //3.2 验证用户名
            User user = userService.getUser(accessToken.getName());
            if(ObjectUtils.isEmpty(user)){
                log.warn("用户不存在");
                this.noAuth(response,"认证失败");
                return false;
            }
            //重新生成token返回前端
            accessToken = new AccessToken(accessToken.getName());
            System.out.println(accessToken.getName());//

            response.setHeader(TOKEN,EncryptorUtil.encrypt(JSONObject.toJSONString(accessToken)));
            return true;

        }catch(Exception e){
            log.error(e.getMessage(),e);
            this.noAuth(response,"服务器异常");
            return false;
        }
    }

    private void noAuth(HttpServletResponse response,String message) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter pw = response.getWriter();
        pw.print(JSONObject.toJSONString(ResultVo.noAuth(message)));
        pw.flush();
        pw.close();
    }
}

配置拦截器

@Configuration
public class MvcConfig implements WebMvcConfigurer {
 @Autowired
    LoginInterseptor loginInterseptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterseptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")// 登录请求放行
                .excludePathPatterns("/pics/**");//   访问图片放行
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值