SpringBoot+JWT@注解实现token验证
原文链接:https://segmentfault.com/a/1190000022776284?utm_source=tag-newest
springboot集成jwt实现token验证
1、引入jwt依赖
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.9.0</version>
</dependency>
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<User> {
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、认证通过则可以访问
- public class JWTUtil {
- public static final String SECRET_KEY = "123456"; //秘钥
- public static final long TOKEN_EXPIRE_TIME = 5 * 60 * 1000; //token过期时间
- public static final long REFRESH_TOKEN_EXPIRE_TIME = 10 * 60 * 1000; //refreshToken过期时间
- private static final String ISSUER = "issuer"; //签发人
- /**
- * 生成签名
- */
- public static String generateToken(String username){
- Date now = new Date();
- Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法
- String token = JWT.create()
- .withIssuer(ISSUER) //签发人
- .withIssuedAt(now) //签发时间
- .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME)) //过期时间
- .withClaim("username", username) //保存身份标识
- .sign(algorithm);
- return token;
- }
- /**
- * 验证token
- */
- public static boolean verify(String token){
- try {
- Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); //算法
- JWTVerifier verifier = JWT.require(algorithm)
- .withIssuer(ISSUER)
- .build();
- verifier.verify(token);
- return true;
- } catch (Exception ex){
- ex.printStackTrace();
- }
- return false;
- }
- /**
- * 从token获取username
- */
- public static String getUsername(String token){
- try{
- return JWT.decode(token).getClaim("username").asString();
- }catch(Exception ex){
- ex.printStackTrace();
- }
- return "";
- }
- }
分类:
springboot
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2018-09-11 svn安装与使用