Spring Boot整合 JWT
简介
使用OAuth2协议实现统一认证会非常烦锁,而且会影响到性能。
直接利用一个Token数据实现分布式认证信息的存储,简单且难度低,token数据量小网络传输更快
一、 配置JWT相关信息
1、引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、配置jwt相关属性
server:
port: 8083
spring:
application:
name: carbon #应用名称
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/carbon1?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: 123456
mybatis:
type-aliases-package: com.dz.carbon.pojo
mapper-locations: classpath:mapper/*Mapper.xml
carbon: #自定义配置
config: #自定义配置项
jwt:
sign: carbon #JWT 证书相关签名
issuer: ranzong # 证书签发人
secret: ranzongCarBon #密钥
expire: 100 #有效时长
3、创建JWT属性配置类,接收application中属性内容
@ConfigurationProperties(prefix = "carbon.config.jwt")
@Component
@Data
public class JWTConfigProperies {
private String sign; // 证书签名信息
private String issuer; // 签名信息
private String secret; // 加密密钥
private long expire; // 失效时间(单位:s)
}
二、编写JWTUtils类
1、注入jwt属性配置类,定义应用名,采用HS256算法
2、设置加密密钥信息(考虑安全性,额外通过base64加密)
public static SecretKey generalKey(){
byte[] encodedKey = Base64.decodeBase64(Base64.encodeBase64(jwtConfigProperies.getSecret().getBytes()));
return new SecretKeySpec(encodedKey,0,encodedKey.length,"AES");
}
3、创建生成token的方法
public static String createToken(Map<String, Object> map){
// 创建jwt
try {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, jwtConfigProperies.getExpire());
Map<String,Object> headers = new HashMap<String,Object>();
headers.put("author","代总");
headers.put("module",applicationName);
return Jwts.builder()
.setSubject(new ObjectMapper().writeValueAsString(map)) // 用户信息
.setIssuedAt(new Date()) //签发时间
.setExpiration(calendar.getTime()) //过期时间
.setHeader(headers) //头部信息
.setId("carbon"+ UUID.randomUUID()) //唯一标识
.signWith(signatureAlgorithm,generalKey())
.compact(); // 创建token
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
4、创建验证token合法性方法
public static boolean verify(String token){
try{
Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token).getBody();
return true;
}
catch (Exception e){
e.printStackTrace();
}
return false;
}
5、创建获取token信息方法
public static Jws<Claims> getTokenInfo(String token){
if (StringUtils.isNullOrEmpty(token)) return null;
return Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token);
}
6、创建刷新token的方法
public String refresh(String token){
// 验证token
if (!verify(token)) return null;
ObjectMapper mapper = new ObjectMapper();
try {
Map<String,Object> map = mapper.readValue(Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token).getBody().getSubject(), HashMap.class);
return createToken(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
7、创建验证token并返回结果
// 验证token获取状态
public static Result checkToken(String token){
try{
Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token).getBody();
}
catch (ExpiredJwtException e){
return new Result(false,StatusCode.ERROR.getCode(), "token过期!");
}
catch (Exception e){
return new Result(false,StatusCode.ERROR.getCode(), "token无效!");
}
return new Result(true,StatusCode.SUCCESS.getCode(), "token有效");
}
8、编写测试类,测试Jwt业务方法
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@SpringBootTest(classes = CarbonApplication.class)
public class TestToken {
@Autowired
private UserService userService;
@Test
public void testCreateToken (){
Map<String,Object> map = new HashMap<String,Object>();
map.put("user",userService.queryById(1));
System.out.println(JWTUtils.createToken(map));
}
@Test
public void testgetToken () throws JsonProcessingException {
Jws<Claims> tokenInfo = JWTUtils.getTokenInfo("eyJhdXRob3IiOiLku6PmgLsiLCJtb2R1bGUiOiJjYXJib24iLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJ1c2VyXCI6e1widWlkXCI6MSxcInVzZXJuYW1lXCI6XCJhZG1pblwiLFwiZW1haWxcIjpcIjEyMzQ1NkBxcS5jb21cIixcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcImF2YXRhclwiOlwiXCJ9fSIsImlhdCI6MTY5ODIxOTM1MSwiZXhwIjoxNjk4MjE5NDUxLCJqdGkiOiJjYXJib25jZmUxZTM2ZC04YThlLTQ4YjYtYWJiMS0xYzhjZTE3OTYzZWUifQ.T_tK9nMdBoZbvgnZKdk6VVyoQOpYS3ST28cmhooG91g");
System.out.println(tokenInfo.getHeader());
System.out.println(tokenInfo.getSignature());
System.out.println(tokenInfo.getBody());
ObjectMapper mapper = new ObjectMapper();
Map map = mapper.readValue(tokenInfo.getBody().getSubject(), HashMap.class);
System.out.println(map.get("user"));
}
@Test
public void testVerify(){
System.out.println(JWTUtils.verify("eyJhdXRob3IiOiLku6PmgLsiLCJtb2R1bGUiOiJjYXJib24iLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJ1c2VyXCI6e1widWlkXCI6MSxcInVzZXJuYW1lXCI6XCJhZG1pblwiLFwiZW1haWxcIjpcIjEyMzQ1NkBxcS5jb21cIixcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcImF2YXRhclwiOlwiXCJ9fSIsImlhdCI6MTY5ODIxODY0NywiZXhwIjoxNjk4MjE4NzQ3LCJqdGkiOiJjYXJib24yYjQ3NTE2Mi0xYjAzLTQ3NGYtYTZlYS1lMGM3MTUzMTkxNDkifQ.XSCrlcB6fRqfnhd7-rUbKG5-F_PZplX76R5D5YTpoMo"));
}
@Test
public void testRefresh(){
System.out.println(JWTUtils.refresh("eyJhdXRob3IiOiLku6PmgLsiLCJtb2R1bGUiOiJjYXJib24iLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJ1c2VyXCI6e1widWlkXCI6MSxcInVzZXJuYW1lXCI6XCJhZG1pblwiLFwiZW1haWxcIjpcIjEyMzQ1NkBxcS5jb21cIixcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcImF2YXRhclwiOlwiXCJ9fSIsImlhdCI6MTY5ODIxOTA0NSwiZXhwIjoxNjk4MjE5MTQ1LCJqdGkiOiJjYXJib24yYTU4NjAwYS0wYjM0LTQyOTYtODg4OS02MWM4ZjU5ZDkyNjAifQ.isOUg0sQl3mq5VQae0Pk7eEjs9GrCRiruNr1HIoVsf0"));
}
三、Token拦截
1、创建拦截器类
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Result result = JWTUtils.checkToken(request.getHeader("X-Token"));
if (!result.getFlag()) return true;
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
return false;
}
}
2、添加拦截器配置类
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}