项目描述
上传时间
浏览人数
一.通过在pom文件中添加JWT依赖,调用token功能
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
二.在config文件夹下创建TokenConfig文件
三.在TokenConfig文件中定义两个注释,通过这两个注释来使用token功能
public class TokenConfig {
//定义跳过token验证的注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken{
boolean required() default true;
}
//定义需要Token验证的注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken{
boolean required() default true;
}
}
四.创建三层架构(controller-service-dao),和pojo后续测试token时使用
1.pojo文件内的操作,可以下载lombok插件简化代码,先在pom添加lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
2.下载lombok插件,idea左上角File-->Settings
3.在pojo文件头部加上这三个注释
@Data
@AllArgsConstructor
@NoArgsConstructor
以代替
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AdminsPojo {
private Long id;
private String phone;
private String password;
private String token;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookPojo {
private Long id;
private String bookname;
private String author;
private String publisher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonMsgPojo {
private int status;
private String msg;
private Object data;
}
五.创建utils文件夹,在内部创建TokenTools文件来生成token密文(封装函数)
public class TokenTools {
//生成token密文的函数
public static String getToken(AdminsPojo ap) {
Date start = new Date();//开始生效时间
long currentTime = System.currentTimeMillis() + 60*60*60 * 1000;//60小时有效时间(根据项目要求)
Date end = new Date(currentTime); //失效时间
//在这里使用了用户的id和密码以生成一个token
String token = JWT.create().withAudience(ap.getPhone().toString()).withIssuedAt(start).withExpiresAt(end)
.sign(Algorithm.HMAC256(ap.getPassword()));
return token;
}
}
六.创建拦截器函数(在访问功能函数前会判断是否有token注解,有则验证,没有就跳过)
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private AdminService adminService;
@Override
//prehandle函数会在Controller里的函数被调用之前提前调用
//所以在这里可以达到提前拦截的效果
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从 http 请求头中取出 token
String token = request.getHeader("token");
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();//获取到被注解的函数
//检查函数是否有@PassToken注解,有则直接跳过认证
if (method.isAnnotationPresent(TokenConfig.PassToken.class)) {
TokenConfig.PassToken passToken = method.getAnnotation(TokenConfig.PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查函数是否有UserLoginToken注解, 有则执行验证, 无则执行return true
if (method.isAnnotationPresent(TokenConfig.UserLoginToken.class)) {
TokenConfig.UserLoginToken userLoginToken = method.getAnnotation(TokenConfig.UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("当前无token信息,请重新登录!");
}
// 获取token中的phone----------phone为自定义的字段,作为登录账号使用
String phone;
try {
phone = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("Token获取失败或有误!");
}
//通过token中的phone来验证是否存在----------phone为自定义的字段,作为登录账号使用
AdminsPojo ap = new AdminsPojo();
ap.setPhone(phone);
AdminsPojo adp = adminService.findAdminsById(ap);
if (adp == null) {
throw new RuntimeException("当前用户ID不存在,请重新登录!");
}
// 通过token中的password与最新查询到password对比 来验证token的正确性----------password为自定义的字段,作为登录账号使用
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(adp.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("Token获取失败或已过期!");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object o, Exception e) throws Exception {
}
}
七.添加拦截器函数的配置文件(设定拦截的访问路径范围)
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**");
//这里的 /** 代表的是拦截所有的访问路径
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}
八.测试token功能
1.在登录函数中加入跳过token验证的注释
@PostMapping("login")
@ResponseBody
@TokenConfig.PassToken
public JsonMsgPojo login(AdminsPojo ap) {
AdminsPojo adp = adminService.login(ap);
if (adp == null) {
return new JsonMsgPojo(500, "用户名或密码错误!", null);
}
String token = TokenTools.getToken(ap);
ap.setToken(token);
return new JsonMsgPojo(200, "登陆成功!", ap);
}
2.返回的结果中会携带token
3.复制token的数据,下一个功能测试时需要用到(每次登录生成的token数据都不同)
4.全查询函数,需要附加token验证
@GetMapping("getbooks")
@ResponseBody
@TokenConfig.UserLoginToken
public JsonMsgPojo getbooks() {
List<BookPojo> bp = adminService.getbooks();
if (bp == null) {
return new JsonMsgPojo(500, "不存在!", null);
}
return new JsonMsgPojo(200, "查询成功!", bp);
}
5.在headers中加入token字段和相应数据
6.若是没在Headers中加入token字段