当前位置 博文首页 > 俺叫啥好嘞的博客:Springboot整合JWT实现鉴权
JWT鉴别身份的流程大概为:用户登录成功后Api服务器将会生成一串包含用户信息的token,将token发送至客户端机器,由客户端进行保存,当客户端机器每次访问JWT所保护的API时都需要进行验证,服务器端便会获取JWT信息,在通过服务器对JWT中Payload部分的信息进行加密,对比实际加密出来的结果是否一致,如果一致,则通过验证。
1.生成token:
用户登录——>成功(如果失败,则返回登录)——>生成token——>将某些用户属性进行加密存放在token中——>通过http响应response将token返回给客户端进行保存
2.身份鉴别:
访问JWT保护的URL——>获取请求头中携带的Authorization属性值——>进行token有效验证(失效则重新登录)——>成功访问资源
因为每次访问资源都需要进行鉴别,所以每次请求中都需要携带Authorization属性
功能实现
1.定义注解
@AdminLoginToken
admin登录认证注解,如果URL含有@AdminLoginToken,则受到@AdminLoginToken保护,需要进行JWT验证
/**
* admin登录检查注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminLoginToken {
boolean required() default true;
}
@PassToken
跳过检查注解,如果URL含有@PassToken,则跳过,不需要执行认证
/**
* 跳过认证注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
2.创建实体类
创建用于认证的实体Admin,属性包括adminId,adminName,adminPassword
@Data
public class Admin implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3.实现拦截
InterceptorConfig类
用于拦截所有的客户端请求,将拦截下的请求转至AuthenticationInterceptor类,进行下一步鉴别,查看请求是否公开,或是受到JWT保护。
/**
* 拦截配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
AuthenticationInterceptor类
用于鉴别API是否受JWT保护,如果不受则释放对应API的拦截,如果受保护则执行JWT验证,验证通过则释放API,验证不通过则返回401。
/**
* 身份验证拦截
*/
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
IAdminService adminService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(AdminLoginToken.class)) {
AdminLoginToken adminLoginToken = method.getAnnotation(AdminLoginToken.class);
if (adminLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 adminId
String adminId;
try {
adminId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
QueryWrapper queryWrapper=new QueryWrapper<>();
queryWrapper.eq("adminId",adminId);
Admin admin = adminService.getOne(queryWrapper);
if (admin == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(admin.getPassWord())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
return true;
}
}
return true;
}
}
4.异常处理
由于token验证不通过,拦截后是抛出对应异常,为了防止程序crash,所以需要进行异常处理
/**
* 全局异常处理类
*/
@ControllerAdvice
public class GloablExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public Object handleException(Exception e) {
String msg = e.getMessage();
if (msg == null || msg.equals("")) {
msg = "服务器出错";
}
ObjectRestResponse object = new ObjectRestResponse();
object.data(msg);
return object;
}
}
5.生成token
TokenService类
用于生成以adminPassword作为密钥的token
@Service("TokenService")
public class TokenService {
public String getToken(Admin admin) {
String token="";
token= JWT.create().withAudience(admin.getAdminName())// 将 adminId 保存到 token 里面
.sign(Algorithm.HMAC256(admin.getAdminPassword()));// 以 adminPassword 作为 token 的密钥
return token;
}
}
6.控制层
AdminController类
编写AdminController,实现admin登录,以及admin身份验证
@RestController
@RequestMapping("/admin")
public class AdminController {
@Autowired
IAdminService adminService;
@Autowired
TokenService tokenService;
@PostMapping("/login")
public ObjectRestResponse login(String username, String password){