springboot集成jwt实现token验证
1、引入jwt依赖
io.jsonwebtoken
jjwt
0.9.0
com.auth0
java-jwt
3.9.0
2、自定义两个注解
/**
* 忽略Token验证
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreAuth {
boolean required() default true;
}
/**
* 登录用户信息
*
*/
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
boolean required() default true;
}
@Target:注解的作用目标
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.METHOD)——方法
3、定义一个用户实体类
/**
* 用户类
*
*/
@Data
@ApiModel(value="User对象", description="用户表")
public class User extends BaseEntity {
private static final long serialVersionUID=1L;
@ApiModelProperty(value = "编号")
private String id;
@ApiModelProperty(value = "归属公司")
private String companyId;
@ApiModelProperty(value = "归属部门")
private String officeId;
@ApiModelProperty(value = "登录名")
private String loginName;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "工号")
private String no;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "电话")
private String phone;
@ApiModelProperty(value = "手机")
private String mobile;
@ApiModelProperty(value = "用户类型")
private String userType;
@ApiModelProperty(value = "用户头像")
private String photo;
@ApiModelProperty(value = "最后登陆IP")
private String loginIp;
@ApiModelProperty(value = "最后登陆时间",example = "2019-11-22 00:00:00")
private Date loginDate;
@EnumFormat
@ApiModelProperty(value = "登录状态 : 0 正常,1 异常")
private UserLoginFlagEnum loginFlag;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "创建时间",example = "2019-11-22 00:00:00")
private Date createDate;
@ApiModelProperty(value = "更新者")
private String updateBy;
@ApiModelProperty(value = "更新时间",example = "2019-11-22 00:00:00")
private Date updateDate;
@ApiModelProperty(value = "备注信息")
private String remarks;
@TableLogic
@ApiModelProperty(value = "删除标记")
private String delFlag;
@ApiModelProperty(value = "微信openid")
private String openid;
@Override
protected Serializable pkVal() {
return this.id;
}
}
4、生成token
@Service("TokenService")
public class TokenService {
public String getToken(User user) {
String token="";
token= JWT.create().withAudience(user.getId())// 将 user id 保存到 token 里面
.sign(Algorithm.HMAC256(user.getOpenid()));// 以 OpenId 作为 token 的密钥
return token;
}
}
5、设置拦截器
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Autowired
IUserService userService;
public static final String LOGIN_USER_KEY = "LOGIN_USER_KEY";
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
//支持跨域请求
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,X-Nideshop-Token,X-URL-PATH");
httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有IgnoreAuth注释,有则跳过认证
if (method.isAnnotationPresent(IgnoreAuth.class)) {
IgnoreAuth passToken = method.getAnnotation(IgnoreAuth.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(LoginUser.class)) {
LoginUser userLoginToken = method.getAnnotation(LoginUser.class);
if (userLoginToken !=null) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
//设置userId到request里,后续根据userId,获取用户信息
httpServletRequest.setAttribute(LOGIN_USER_KEY, userId);
User user = userService.getById(userId);
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getOpenid())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
6、配置拦截器
在配置类上添加了注解@Configuration
,标明了该类是一个配置类并且会将该类作为一个SpringBean
添加到IOC
容器内
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**");
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
7、token验证流程
1、用户登录是生成token
2、从http请求头中取出token
3、判断是否映射到方法
4、检查是否有@IgnoreAuth注释,有则跳过认证
5、检查是否有用户登录的注解,有则需要取出并验证
6、认证通过则可以访问