JWT认证原理、流程整合springboot实战应用,前后端分离认证的解决方案!
https://www.bilibili.com/video/BV1i54y1m7cP?p=1
https://blog.csdn.net/qq_41347385/article/details/109624658
https://blog.csdn.net/u014204541/article/details/103906208
1.JwtUtil工具类
package com.example.demo.util;
/**
* @ClassName JwtUtil
* @Description TODO
* @Author Administrator
* @Date 2021/02/01 15:04:33
* @Verison 1.0
*/
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.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import org.apache.commons.lang3.StringUtils;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
//过期时间 3天
private static final long EXPIRE_TIME = 3 * 24 * 60 * 60 * 1000;
//私钥
private static final String TOKEN_SECRET = "huiming@7122^$";
/**
* 生成签名,15分钟过期
* 根据内部改造,支持6中类型,Integer,Long,Boolean,Double,String,Date
* @param map
* @return
*/
public static String sign(Map<String,Object> map) {
try {
// 设置过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
// 私钥和加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
// 设置头部信息
Map<String, Object> header = new HashMap<>(2);
header.put("typ", "jwt");
// 返回token字符串
JWTCreator.Builder builder = JWT.create()
.withHeader(header)
.withIssuedAt(new Date()) //发证时间
.withExpiresAt(date); //过期时间
// .sign(algorithm); //密钥
map.entrySet().forEach(entry -> {
if (entry.getValue() instanceof Integer) {
builder.withClaim( entry.getKey(),(Integer)entry.getValue());
} else if (entry.getValue() instanceof Long) {
builder.withClaim( entry.getKey(),(Long)entry.getValue());
} else if (entry.getValue() instanceof Boolean) {
builder.withClaim( entry.getKey(),(Boolean) entry.getValue());
} else if (entry.getValue() instanceof String) {
builder.withClaim( entry.getKey(),String.valueOf(entry.getValue()));
} else if (entry.getValue() instanceof Double) {
builder.withClaim( entry.getKey(),(Double)entry.getValue());
} else if (entry.getValue() instanceof Date) {
builder.withClaim( entry.getKey(),(Date)entry.getValue());
}
});
return builder.sign(algorithm);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检验token是否正确
* @param **token**
* @return
*/
public static JWTVerifier verify(String token){
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return verifier;
}
/**
*获取用户自定义Claim集合
* @param token
* @return
*/
public static Map<String, Claim> getClaims(String token){
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
Map<String, Claim> jwt = verifier.verify(token).getClaims();
return jwt;
}
/**
* 获取过期时间
* @param token
* @return
*/
public static Date getExpiresAt(String token){
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
return JWT.require(algorithm).build().verify(token).getExpiresAt();
}
/**
* 获取jwt发布时间
*/
public static Date getIssuedAt(String token){
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
return JWT.require(algorithm).build().verify(token).getIssuedAt();
}
/**
* 验证token是否失效
*
* @param token
* @return true:过期 false:没过期
*/
public static boolean isExpired(String token) {
try {
final Date expiration = getExpiresAt(token);
return expiration.before(new Date());
}catch (TokenExpiredException e) {
// e.printStackTrace();
return true;
}
}
/**
* 直接Base64解密获取header内容
* @param token
* @return
*/
public static String getHeaderByBase64(String token){
if (StringUtils.isEmpty(token)){
return null;
}else {
byte[] header_byte = Base64.getDecoder().decode(token.split("\\.")[0]);
String header = new String(header_byte);
return header;
}
}
/**
* 直接Base64解密获取payload内容
* @param token
* @return
*/
public static String getPayloadByBase64(String token){
if (StringUtils.isEmpty(token)){
return null;
}else {
byte[] payload_byte = Base64.getDecoder().decode(token.split("\\.")[1]);
String payload = new String(payload_byte);
return payload;
}
}
}
2.依赖和数据库
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
mapper-locations: classpath:/mybatis/mapper/*.xml
type-aliases-package: com.example.demo.dao
server:
port: 8123
spring:
application:
name: table-server
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/ssmbuild?useUnicode=true&characterEncoding=utf8&useSSL=true&nullCatalogMeansCurrent=true&serverTimezone=Asia/Shanghai
username: root
3. 实现代码
UserDao.class
@Mapper
public interface UserDAO {
//直接根据用户名密码登录
User login(User user);
}
User.class
@Data
@Accessors(chain = true)
public class User {
private int userid;
private String name;
private String password;
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.UserDAO">
<select id="login" parameterType="com.example.demo.domain.User" resultType="com.example.demo.domain.User">
select * from user where name =#{name} and password = #{password};
</select>
</mapper>
UserController.class
@RestController
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/doLogin")
public Map<String, Object> login(User user){
log.info("id 是[{}]", user.getUserid());
log.info("用户名:[{}]", user.getName());
log.info("密码:[{}]", user.getPassword());
Map<String, Object> map = new HashMap<>();
try{
User userDB = userService.login(user);
Map<String,Object> payload = new HashMap<>();
payload.put("id", userDB.getUserid());
payload.put("name", userDB.getName());
String token = JwtUtil.sign(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(String token){
Map<String, Object> map = new HashMap<>();
log.info("当前token为:[{}]",token);
JWTVerifier verify = JwtUtil.verify(token);
map.put("state", true);
map.put("msg", "请求成功");
return map;
}
}
过滤器JWTInterceptor.class
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
Map<String, Object> map = new HashMap<>();
try {
JWTVerifier verify = JwtUtil.verify(token);
map.put("state", true);
map.put("msg", "请求成功");
}catch (SignatureGenerationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","token算法不一致");
}
catch (Exception e) {
e.printStackTrace();
map.put("msg","token无效");
}
map.put("state", false); // 设置状态
// 将map转为json
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
配置过滤器 InterceptorConfig.class
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/doLogin");// 除了登录放行,其他接口都token验证
}
}