SpringBoot与JWT整合方式
作者:BLUcoding
文章介绍了如何在Spring Boot项目中整合JWT(JSON Web Token),包括JWT的结构、使用方法、测试以及配置,主要内容涵盖了依赖配置、数据库表设计、实体类、数据访问层、服务层、JWT工具类、拦截器配置和控制器测试等多个方面
SpringBoot与JWT整合
JWT的结构
Header(头):包含令牌的类型与使用的签名算法,它会使用Base64进行编码
{ "alg": "HS265", "typ": "JWT" }
Payload(有效负载): 包含声明(有关用户实体和其他数据的声明),使用Base64进行编码Base64是一种可逆的编码,因此不要在负载里存入敏感数据!
{ "id": "1", "name": "BLU", "admin": true }
Signature(签名):使用编码后的header和payload以及一个指定密钥,然后使用header中指定的算法(HS265)进行签名.
签名的作用是保证JWT没有被篡改过
JWT的使用测试
- 依赖:
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency>
- 测试:
@Test void getToken() { //HashMap<String,Object> map = new HashMap<String, Object>(); Calendar instance = Calendar.getInstance(); instance.add(Calendar.SECOND, 60); String token = JWT.create() //.withHeader(map) //设置Payload .withClaim("userId", 10) .withClaim("username", "BLU") //设置token过期时间(60s) .withExpiresAt(instance.getTime()) //设置签名 .sign(Algorithm.HMAC256("!@#$%^&*")); System.out.println(token); }
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDA3NDA2MTUsInVzZXJJZCI6MTAsInVzZXJuYW1lIjoiQkxVIn0.0re-tA4dQm4blGhn1DvpnUl7Lrz_EWXwn8LfRbWQXCU
@Test void TokenVerify() { //创建验证对象 JWTVerifier verifier = JWT.require(Algorithm.HMAC256("!@#$%^&*")).build(); DecodedJWT verify = verifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDA3NDA2MTUsInVzZXJJZCI6MTAsInVzZXJuYW1lIjoiQkxVIn0.0re-tA4dQm4blGhn1DvpnUl7Lrz_EWXwn8LfRbWQXCU"); System.out.println(verify.getClaim("userId").asInt()); System.out.println(verify.getClaims().get("username").asString()); System.out.println("过期时间:"+verify.getExpiresAt()); }
10
BLU
过期时间:Tue Sep 22 10:10:15 CST 2020
SpringBoot与JWT整合
- 依赖
<dependencies> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
- 配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/jwt?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=123456 mybatis.type-aliases-package=com.blu.entity mybatis.mapper-locations=classpath:mapper/*.xml logging.level.com.blu.dao=debug
- 数据库user表:
- 实体类:
package com.blu.entity; import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain=true) public class User { private String id; private String name; private String password; }
- UserDao:
package com.blu.dao; import org.apache.ibatis.annotations.Mapper; import com.blu.entity.User; @Mapper public interface UserDao { User login(User user); }
- UserMapper:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.blu.dao.UserDao"> <select id="login" parameterType="User" resultType="User"> select * from user where name= #{name} and password= #{password} </select> </mapper>
- UserService:
package com.blu.service; import com.blu.entity.User; public interface UserService { User login(User user); }
- UserServiceImpl:
package com.blu.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.blu.dao.UserDao; import com.blu.entity.User; import com.blu.service.UserService; @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User login(User user) { User userDB = userDao.login(user); if(userDB!=null) { return userDB; } throw new RuntimeException("认证失败!"); } }
- JWT的工具类封装:
package com.blu.utils; import java.util.Calendar; import java.util.Map; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; public class JWTUtils { private static final String SIGN= "!@#$%^&*123456789"; /** * 生成Token */ public static String getToken(Map<String,String> map) { Calendar instance = Calendar.getInstance(); instance.add(Calendar.DATE, 7); //创建JWTBuilder JWTCreator.Builder builder = JWT.create(); //设置payload map.forEach((k,v)->{ builder.withClaim(k, v); }); //设置过期时间和签名,生成token String token = builder.withExpiresAt(instance.getTime()) .sign(Algorithm.HMAC256(SIGN)); return token; } /** * 验证token */ public static void TokenVerify(String token) { JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); } /** * 获取token信息 */ public static DecodedJWT getTokenInfo(String token) { DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); return verify; } }
- JWTInterceptor 拦截器
package com.blu.interceptors; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import com.auth0.jwt.exceptions.AlgorithmMismatchException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.blu.utils.JWTUtils; import com.fasterxml.jackson.databind.ObjectMapper; public class JWTInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求头中的token String token = request.getHeader("token"); Map<String,Object> map = new HashMap<String, Object>(); try { JWTUtils.TokenVerify(token); //放行请求 return true; } catch (SignatureVerificationException e) { map.put("msg", "无效签名"); e.printStackTrace(); } catch (TokenExpiredException e) { map.put("msg", "token已过期"); e.printStackTrace(); } catch (AlgorithmMismatchException e) { map.put("msg", "算法不一致"); e.printStackTrace(); } catch (Exception e) { map.put("msg", "token无效"); e.printStackTrace(); } map.put("state",false); //使用jackson将map转为json String json = new ObjectMapper().writeValueAsString(map); response.setContentType("application/json;charset=UTF-8"); response.getWriter().print(json); return false; } }
- InterceptorConfig 配置类
package com.blu.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.blu.interceptors.JWTInterceptor; @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JWTInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/user/login"); } }
- UserController
package com.blu.controller; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; 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.RestController; import com.auth0.jwt.exceptions.AlgorithmMismatchException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import com.blu.entity.User; import com.blu.service.UserService; import com.blu.utils.JWTUtils; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class UserController { @Autowired private UserService userService; @GetMapping("/user/login") public Map<String,Object> login(User user){ log.info("用户名:[{}]",user.getName()); log.info("密码:[{}]",user.getPassword()); Map<String,Object> map = new HashMap<String, Object>(); try { User userDB = userService.login(user); Map<String,String> payload = new HashMap<String, String>(); payload.put("id", userDB.getId()); payload.put("name", userDB.getName()); String token = JWTUtils.getToken(payload); map.put("state",true); map.put("msg","认证成功"); map.put("token", token); } catch (Exception e) { map.put("state",false); map.put("msg",e.getMessage()); } return map; } @PostMapping("/user/test") public Map<String,Object> test(HttpServletRequest request){ String token = request.getHeader("token"); DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token); log.info("用户id:[{}]",tokenInfo.getClaim("id").asString()); log.info("用户名:[{}]",tokenInfo.getClaim("name").asString()); Map<String,Object> map = new HashMap<String, Object>(); map.put("state", true); map.put("msg","请求成功"); return map; } }
- 测试
登录获取token:
测试错误的token:
测试正确的token:
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。