参考自文章
Springboot+Vue前后端分离实现token登录验证和状态保存
1. 后端Springboot
目录结构
image1.1 拦截器LoginInteceptor类 (com.chinasoft.config)包
package com.chinasoft.config;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.chinasoft.annotation.PassToken;
import com.chinasoft.annotation.UserLoginToken;
import com.chinasoft.pojo.User;
import com.chinasoft.service.UserService;
/**
* 未登录拦截器
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("Authorization");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证,无token
if (token == null) {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
try{
JSONObject json = new JSONObject();
json.put("msg","token verify fail");
json.put("code","50000");
httpServletResponse.getWriter().append(json.toJSONString());
}catch (Exception e){
e.printStackTrace();
httpServletResponse.sendError(500);
return false;
}
return false;
}
// 获取 token 中的 user id
String userId = null;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
try{
JSONObject json = new JSONObject();
json.put("msg","usertoken verify fail");
json.put("code","401");
httpServletResponse.getWriter().append(json.toJSONString());
System.out.println("身份验证错误");
}catch (Exception e){
e.printStackTrace();
httpServletResponse.sendError(401);
return false;
}
return false;
}
// 获取用户
int id = Integer.parseInt(userId);
User user = userService.findOneUserById(id);
if (user == null) {
// 返回错误信息
try{
JSONObject json = new JSONObject();
json.put("msg","no user");
json.put("code","50000");
httpServletResponse.getWriter().append(json.toJSONString());
System.out.println("不存在该用户");
}catch (Exception e){
e.printStackTrace();
httpServletResponse.sendError(500);
return false;
}
return false;
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
DecodedJWT jwt = jwtVerifier.verify(token);
System.out.println("认证通过:");
System.out.println("过期时间: " + jwt.getExpiresAt());
} catch (JWTVerificationException e) {
try{
JSONObject json = new JSONObject();
json.put("msg","token has expired");
json.put("code","401");
httpServletResponse.getWriter().append(json.toJSONString());
System.out.println("token已过期");
}catch (Exception ex){
ex.printStackTrace();
httpServletResponse.sendError(401);
return false;
}
return false;
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
1.2 注册拦截器LoginInteceptor(com.chinasoft.config包)
package com.chinasoft.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 注册拦截器
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginHandlerInterceptor)
.addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
}
@Bean
public LoginInterceptor loginHandlerInterceptor() {
return new LoginInterceptor();
}
}
1.3 在TokenServiceImpl类中创建生成Token方法(包com.chinasoft.service.impl)
package com.chinasoft.service.impl;
import java.util.Date;
import org.springframework.stereotype.Service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.chinasoft.pojo.User;
import com.chinasoft.service.TokenService;
@Service
public class TokenServiceImpl implements TokenService{
@Override
public String getToken(User user) {
Date start = new Date();
long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
Date end = new Date(currentTime);
String token = "";
// 生成token,开始时间,结束时间
token = JWT.create().withAudience(user.getUid() + "").withIssuedAt(start).withExpiresAt(end)
.sign(Algorithm.HMAC256(user.getPassword()));
return token;
}
}
1.4 跳过身份验证的注解@PassToken 和需要身份验证的注解 @UserLoginToken(包com.chinasoft.annotation)
PassToken
package com.chinasoft.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author :ZWQ
* @version :1.0
* @date :2019/10/16 - 18:44
* @description :用来跳过验证的PassToken
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
UserLoginToken
package com.chinasoft.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author :ZWQ
* @version :1.0
* @date :2019/10/16 - 18:46
* @description :需要登录才能进行操作的注解UserLoginToken
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
1.5 Controller中的login方法(包com.chinasoft.controller)
import com.chinasoft.service.TokenService;
import com.chinasoft.service.UserService;
@Controller
@RequestMapping("user")
@CrossOrigin
public class UserController {
@Autowired
UserService service;
@Autowired
TokenService tokenService;
// 登录(需要账号密码)
@RequestMapping("login")
@ResponseBody
public Map login(String name, String password, HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> hm = new HashMap<>(); // 通过map返回token和user
User user = service.LoginUser(name, password); // Service类中的方法验证并获取用户
hm.put("user", user);
if (user == null) {
System.out.println("该用户不存在");
return null;
} else {
System.out.println("登录成功");
}
// 生成token并返回
String token = tokenService.getToken(user);
hm.put("token", token);
return hm;
}
}
网友评论