基于springboot 2.0+jwt的登录验证
一、准备环境,jdk8.0+springboot 2.0+jwt 3.4.0+IDEA
二、jwt 的meavn 依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
三、配置WebMvcConfigurer,springboot 2.0之前用的是WebMvcConfigurerAdapter(现在已废弃了)
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
//注入登录拦截器
@Autowired
LoginInterceptor login;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//这得的login用两种方式,一种像本文一样用注入的方式,第二种是用new LoginInterceptor ();但是这用new 的方式的话,在LoginInterceptor 种注入service层会不起作用。所以用注入的方式。
registry.addInterceptor(login)
//添加需要拦截的地址,静态文件也可以再这里做配置,用excludePathPatterns是不拦截
.addPathPatterns("/api/admin/**");
}
}
四、自定两个注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
五、配置登录拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
String token = request.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(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
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");
}
log.info("userId:{}",userId);
UserPojo user = userService.findUserById(userId);
if ( null == user) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getUserPwd())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
六、增加token获取方式
@Override
public String getToken(UserPojo user) {
String token="";
token= JWT.create().withAudience(user.getUserId().toString())
.sign(Algorithm.HMAC256(user.getUserPwd()));
return token;
}
七、登录代码
@RestController
@RequestMapping("/api/admin/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private TokenService tokenService;
@UserLoginToken
@ApiOperation(value = "第一个接口")
@RequestMapping(value ="test",method = RequestMethod.GET)
public BaseResponse first(){
return new BaseResponse(200,"成功","Hello world");
}
@ApiOperation(value = "后台登录接口")
@RequestMapping(value = "/login",method = RequestMethod.POST)
public BaseResponse login(String userName, String userPwd ){
UserPojo user = userService.getUserByUserNameAndUserPwd(userName, userPwd);
if (null != user){
String accessToken = tokenService.getToken(user);
Map<String,Object> map = new HashMap<>();
map.put("token",accessToken);
return new BaseResponse(200,"登录成功",map);
}else {
return new BaseResponse(500,"登录失败,密码或账号错误!",null);
}
}
}
之前自定义的连个注解就是,在需要做登录验证的方法上加入@UserLoginToken就是需要验证,@PassToken就是不需要验证,不写默认是需要验证的。
八、总结
这里的UserPojo就是一个简单的 用户名和密码,数据库也是对应的。
测试的时候直接放问login这个地址,输入用户名和密码;就回返回一个token,可以直接返回代码里的那个map。我这里是写了一个统一的相应类。
测试访问test的时候,把登录返回的token写在header里,就可以显示访问正常,如果不带的话就回返回没有token,在LoginInterceptor里,这里验证了token的有效性,以及token种所携带的用户的有效性。这里的jwt只是做了一个简单的生成方式,感兴趣的可以去单独看看jwt,jwt是可以加盐的,以及token有效期,这些都是可以设置的,然后在登录拦截器里做对应的处理就行。