參考自文章
Springboot+Vue前后端分離實現(xiàn)token登錄驗證和狀態(tài)保存
1. 后端Springboot
目錄結構
image
1.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()) {
// 執(zhí)行認證俄删,無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類中創(chuàng)建生成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;
}
}