Login authority verification

permission system to ensure effective login to do the background permission check

write login interface when logging in successfully pass an encrypted AccessToken in session containing login information and expiration time

  • write a AccessToken class to pack the information
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;
    }
}

encryption and decryption tool class

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);
    }
}

uniform response return format

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,"");
    }
}

login interface:

/**
     * 登录接口
     * 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));
    }

save it to sessionStorage after the

front end receives the AccessToken

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("网络异常");
   });

from this front-end request must be added with a request header token in main.js add request interceptor
return response we also need to get the token in response to add response interceptor
add response interceptor
before logging in we cannot jump route from navigation bar to set global route guard

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')

and in our back end we also need to validate all tokens carried by requests other than login. This requires the request interceptor
LoginInterceptor
interceptor needs to configure

in the configuration class

//登录拦截器
@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();
    }
}

configures interceptor

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

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterseptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")// 登录请求放行
                .excludePathPatterns("/pics/**");//   访问图片放行
    }
}

Read More: