Springboot整合JWT
1. 添加依赖
<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>
<!-- jwt依赖 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
<!--springboot整合mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. 封装JWT工具类
package com.example.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
/**
* 签名,由于生成token和解签时都需要使用sign,所以作为成员变量。
*/
private static final String SIGN = "$%!FDGS^@G!GF!AFDSF&%^F";
private JWTUtils() {
}
/**
* 生成token header.payload.signature
* @param map 用户信息,以 Map<String, String> 类型封装
* @return token字符串
*/
public static String getToken(Map<String, String> map) {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7); // 默认7天过期
//创建 JWTBuilder
JWTCreator.Builder builder = JWT.create();
//header不写则使用默认值
// payload
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
String token = builder
.withExpiresAt(instance.getTime()) //过期时间
.sign(Algorithm.HMAC256(SIGN)); //签名算法
return token;
}
/**
* 验证token是否合法,若不合法则会抛出异常
* @param token token字符串
*/
public static DecodedJWT verifyToken(String token) {
return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
}
/**
* 获取token的信息,通过调用 DecodedJWT 的 get 方法,可以得到 token 的各种信息
* 该方法也可以和验证 token 方法合并
* @param token token字符串
* @return DecodedJWT
*/
/*public static DecodedJWT getTokenInfo(String token) {
return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
}*/
}
3. 使用拦截器验证token
若验证 token 的操作放在 Controller 中,将会有大量的代码冗余。所以验证 token 的操作应该放在拦截器中:
package com.example.interceptors;
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.example.utils.JWTUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();
// jwt不建议作为请求参数,而应该在请求的请求头中
// 获取请求头中的令牌(token)
String token = request.getHeader("token");
try {
DecodedJWT decodedJWT = JWTUtils.verifyToken(token);
// 放行请求
return true;
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "无效签名!");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "token过期!");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "算法不一致!");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效!");
}
map.put("state", false); //设置状态
// 将map转为json,@ResponseBody使用了jackson依赖,所以可以直接使用
String json = new ObjectMapper().writeValueAsString(map);
// 设置相应类型为json,字符集为utf-8
response.setContentType("application/json;charset=UTF-8");
// 返回json
response.getWriter().print(json);
return false;
}
}
4. 配置拦截器
设置拦截器的拦截路径和放行路径:
package com.example.config;
import com.example.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 InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**") //拦截所有路径
.excludePathPatterns("student/login"); // 放行登录接口
}
}
5. 在controller中获取数据
package com.example.controller;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.entity.Student;
import com.example.service.StudentService;
import com.example.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/student")
@Slf4j
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/login")
public Map<String, Object> login(Student student) {
//打印请求参数
log.info(student.getName() + "," + student.getPassword());
//map封装返回json数据
Map<String, Object> map = new HashMap<>();
try {
Student studentDB = studentService.login(student);
//封装payload信息
Map<String, String> payload = new HashMap<>();
payload.put("id", studentDB.getId());
payload.put("name", studentDB.getName());
//生成JWT令牌
String token = JWTUtils.getToken(payload);
map.put("state", true);
map.put("msg", "认证成功");
//将token返回前端
map.put("token", token);
} catch (Exception e) {
e.printStackTrace();
map.put("state", false);
map.put("msg", "认证失败");
}
return map;
}
/**
* 测试接口,访问该接口必须携带token
* @return
*/
@PostMapping("/test")
public Map<String, Object> test(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
Map<String, Object> data = new HashMap<>();
//处理自己的业务逻辑,由于token已经在拦截器中进行了验证,所以这里获取的token一定是正确的
String token = request.getHeader("token");
DecodedJWT decodedJWT = JWTUtils.verifyToken(token);
Claim id = decodedJWT.getClaim("id");
Claim name = decodedJWT.getClaim("name");
data.put("id", id.asString());
data.put("name", name.asString());
map.put("state", true);
map.put("msg", "请求成功!");
map.put("data", data);
return map;
}
}
6. 使用postman测试接口
注意: token最好不要作为请求参数进行传递,最好放在请求头中。
感谢观看!