1、JWT的構成
- 頭部(header):描述該JWT的最基本的信息,如類型以及簽名所用的算法。
- 負載(payload):存放有效信息的地方。
- 簽證(signature):base64加密后的header、base64加密后的payload和密鑰secret加密后組成。
2、整合JWT
2.1 引入JWT依賴
<dependency>
<groupId>com.auth0</groupId>
<artifactId>JAVA-jwt</artifactId>
<version>3.18.3</version>
</dependency>
2.2 編寫JWTUtils工具類
package com.stock.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
private static final String SING="@#$%^&*";
// 生成token
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.MINUTE,30);
//創建jwt builder
JWTCreator.Builder builder = JWT.create();
//payload
builder.withExpiresAt(instance.getTime());
map.forEach((k,v)->{
builder.withClaim(k,v);
});
//設置簽名
String token = builder.sign(Algorithm.Hmac256(SING));
return token;
}
//驗證令牌
public static void verifyToken(String token){
JWTVerifier require = JWT.require(Algorithm.HMAC256(SING)).build();
require.verify(token);
}
//獲取token信息
public static DecodedJWT getTokenInfo(String token){
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
return verify;
}
}
2.3 編寫攔截器
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
System.out.println("OPTIONS請求,放行");
return true;
}
HashMap<String, Object> map = new HashMap<>();
String token = request.getHeader("token");
try {
JWTUtils.verifyToken(token);
return true;
}catch (SignatureVerificationException e){
map.put("msg","無效簽名!");
}catch (TokenExpiredException e){
map.put("msg","token過期!");
}catch (AlgorithmMismatchException e){
map.put("msg","token加密算法不一致");
}catch (Exception e){
map.put("msg","無效簽名!");
}
map.put("state",404);
map.put("path","/login");
//將map轉化為字符串返回給前端
String result = new ObjectMApper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(result);
return false;
}
}
注意:
1、token存放在請求的header中;
2、在前后端分離的項目中,發送的GET/POST請求實則為兩次請求。第一次請求為OPTIONS請求,第二次請求才是GET/POST請求;在OPTIONS請求中,不會攜帶請求頭的參數,會導致在攔截器上獲取請求頭為空,自定義的攔截器攔截成功。第一次請求不能通過,就不能獲取第二次的請求。所以需要在攔截器中加上如下代碼來判斷是否為OPTIONS請求,對于OPTIONS請求直接放過。
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
System.out.println("OPTIONS請求,放行");
return true;
}
2.4 配置攔截器
package com.stock.config;
import com.stock.Interceptors.JWTInterceptor;
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 IntercepterConfg implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}
2.5 編寫Controller
package com.stock.controller;
import com.stock.entity.User;
import com.stock.result.Result;
import com.stock.service.UserService;
import com.stock.utils.JWTUtils;
import com.stock.utils.ResultUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
public class LoginController {
private UserService userService;
@Autowired
public LoginController(UserService userService) {
this.userService = userService;
}
@PostMapping("/login")
public Result register(User user){
HashMap<String, String> map = new HashMap<>();
map.put("username",user.getUserName());
String token = JWTUtils.getToken(map);
HashMap<String, Object> data = new HashMap<>();
data.put("token",token);
return ResultUtils.getresult(200,"登錄成功!",data);
}
@GetMapping("/main")
public Result tomain(){
return ResultUtils.getresult(200,"訪問成功",null);
}
}
2.6使用Postman測試
- 未登錄前訪問127.0.0.1:8888/main
- 先登錄再訪問127.0.0.1:8888/main