1、Jwt 简介
JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地将信息作 为JSON对象传输。由于此信息是经过数字签名的,因此可以进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或 ECDSA的公钥/私钥对对JWT进行签名。
2、应用场景
3、Jwt结构
JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.
)分隔,分别是:
因此,JWT通常如下所示。
xxxxx.yyyyy.zzzzz
4、Jwt工作流程
1. 用户使用账号和面发出post请求;
2. 服务器使用私钥创建一个jwt;
3. 服务器返回这个jwt给浏览器;
4. 浏览器将该jwt串在请求头中像服务器发送请求;
5. 服务器验证该jwt;
6. 返回响应的资源给浏览器。
5、SpringBoot 集成 Jwt
没有连接数据库
pom配置文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.2.RELEASE
com.example
loginintercept
0.0.1-SNAPSHOT
loginintercept
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
io.jsonwebtoken
jjwt
0.7.0
org.projectlombok
lombok
1.18.12
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
package com.example.loginintercept.config;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import org.springframework.stereotype.Component;
/**
* token 工具类
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/7/30 2:23 下午
*/
@Component
public class JwtUtils {
// 过期时间
private static long expire = 604800;
// 秘钥
private static String secret = "HSyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9";
/**
* 创建一个token
*
* @param userId
* @return
*/
public String generateToken(String userId) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + expire);
return Jwts.builder().setHeaderParam("type", "JWT").setSubject(userId).setIssuedAt(now)
.setExpiration(expireDate).signWith(
SignatureAlgorithm.HS512, secret).compact();
}
/**
* 解析token
*/
public Claims getClaimsByToken(String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
System.out.println("validate is token error");
return null;
}
}
/**
* 判断 token 是否过期
*/
public boolean isTokenExpired(Date expiration){
return expiration.before(new Date());
}
}
package com.example.loginintercept.config;
import java.util.HashMap;
import java.util.Map;
/**
* TODO
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/8/6 5:04 下午
*/
public class ResultT extends HashMap {
public ResultT() {
put("code", 0);
put("msg", "success");
}
public static ResultT ok() {
ResultT t = new ResultT();
t.put("msg", "操作成功");
return t;
}
public static ResultT ok(String msg) {
ResultT t = new ResultT();
t.put("msg", msg);
return t;
}
public static ResultT ok(Map map) {
ResultT t = new ResultT();
t.putAll(map);
return t;
}
public static ResultT error(int code, String msg) {
ResultT t = new ResultT();
t.put("code", code);
t.put("msg", msg);
return t;
}
public static ResultT error() {
return error(500, "未知异常");
}
public ResultT put(String key, Object value){
super.put(key, value);
return this;
}
}
TokenInterceptor 创建一个token拦截器
package com.example.loginintercept.config;
import com.example.loginintercept.exception.TokenRuntimeException;
import io.jsonwebtoken.Claims;
import java.util.Date;
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.servlet.handler.HandlerInterceptorAdapter;
/**
* 创建一个 token 拦截器.
* 需要继承 HandlerInterceptorAdapter,并且声明为spring的组件
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/7/30 2:19 下午
*/
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
// 注入jwt工具类
@Autowired
private JwtUtils jwtUtils;
// 重写 前置拦截方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1、从请求头中获取token
String token = request.getHeader("token");
// 2、判断 token 是否存在
if (token == null ||"".equals(token)) {
System.out.println("未登录");
// 这里可以自定义 抛出 token 异常
throw new TokenRuntimeException("未登录");
}
// 3、解析token
Claims claim = jwtUtils.getClaimsByToken(token);
if (null == claim) {
System.out.println("token 解析错误");
// 这里可以自定义 抛出 token 异常
throw new TokenRuntimeException("token 解析错误");
}
// 4、判断 token 是否过期
Date expiration = claim.getExpiration();
boolean tokenExpired = jwtUtils.isTokenExpired(expiration);
if (tokenExpired) {
System.out.println("token已过期,请重新登录");
// 这里可以自定义 抛出 token 异常
throw new TokenRuntimeException("token已过期,请重新登录");
}
// 5、 从 token 中获取员工信息
String subject = claim.getSubject();
// 6、去数据库中匹配 id 是否存在 (这里直接写死了)
if (null == subject) {
System.out.println("员工不存在");
// 这里可以自定义 抛出 token 异常
throw new TokenRuntimeException("员工不存在");
}
// 7、成功后 设置想设置的属性,比如员工姓名
request.setAttribute("userId", subject);
request.setAttribute("userName", "张三");
return true;
}
}
package com.example.loginintercept.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 设置拦截器.
* 打上configuration 注解,标注为配置项
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/7/30 2:17 下午
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 注入 token 拦截器
@Autowired
private TokenInterceptor interceptor;
/**
* 重写添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加自定义拦截器,并拦截对应 url
registry.addInterceptor(interceptor).addPathPatterns("/gateway/**");
}
}
GatewayController 模仿需要登录后才能访问的资源
package com.example.loginintercept.controller;
import com.example.loginintercept.config.ResultT;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* controller
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/8/6 4:52 下午
*/
@RestController
@RequestMapping("/gateway")
public class GatewayController {
@RequestMapping(value = "/find",method = RequestMethod.POST)
public ResultT find(){
return ResultT.ok("find one success");
}
}
LoginController 登录
package com.example.loginintercept.controller;
import com.example.loginintercept.config.JwtUtils;
import com.example.loginintercept.config.ResultT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* 登录
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/8/6 5:12 下午
*/
@RestController
public class LoginController {
// 注入jwt工具类
@Autowired
private JwtUtils jwtUtils;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResultT login(String name, String psw) {
String userId = "132";
String token = jwtUtils.generateToken(userId);
return ResultT.ok().put("token", token);
}
}
SysRuntimeExceptionHandler 全局异常处理
package com.example.loginintercept.exception;
import com.example.loginintercept.config.ResultT;
import com.example.loginintercept.exception.TokenRuntimeException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/8/6 5:03 下午
*/
@RestControllerAdvice
public class SysRuntimeExceptionHandler {
@ExceptionHandler(TokenRuntimeException.class)
public ResultT tokenRuntimeException(TokenRuntimeException e) {
e.printStackTrace();
return ResultT.error(e.getCode(), e.getMsg());
}
@ExceptionHandler(Exception.class)
public ResultT handlerException(Exception e){
e.printStackTrace();
return ResultT.error();
}
}
TokenRuntimeException 自定义 token 异常
package com.example.loginintercept.exception;
import lombok.Data;
/**
* 自定义 token 异常
*
* @author ch
* @version 1.0.0
* @since 1.0.0
*
* Created at 2020/8/6 4:58 下午
*/
@Data
public class TokenRuntimeException extends RuntimeException{
private Integer code = 401;
private String msg;
public TokenRuntimeException(String msg) {
this.msg = msg;
}
}
6、运行测试
代码地址: [email protected]:ChenHaoXFN/loginintercept.git
https://github.com/ChenHaoXFN/loginintercept
参考文献:https://jwt.io/introduction/
https://www.jianshu.com/p/e88d3f8151db