> 文章列表 > SpringBoot自定义登录、权限验证

SpringBoot自定义登录、权限验证

SpringBoot自定义登录、权限验证

1、首先最基础的User实体类,使用了lombok,所以省略了getter、setter方法

@Data
public class UserInfo implements Serializable {private Integer id;//用户名private String username;//密码不需要被序列化存入redisprivate transient String password;//登录过期时间private Long expireTime;//登录成功后的tokenprivate String token;//权限集private Set<String> permissions;public UserInfo() {}public UserInfo(String username, String password) {this.username = username;this.password = password;}public UserInfo(int id, String username, String password) {this.id = id;this.username = username;this.password = password;}}

2、redis基础操作,登录成功后,信息保存到redis中

@Component
public class RedisCache {@Autowiredpublic RedisTemplate redisTemplate;/*** 缓存基本的对象,Integer、String、实体类等** @param key 缓存的键值* @param value 缓存的值*/public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/*** 缓存带有过期时间的对象* @param key       缓存的键值* @param value     缓存的值* @param time      过期时间* @param timeUnit  时间单位*/public <T> void setCacheObjectExpire(final String key, final T value, final Long time, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key,value,time,timeUnit);}
}

3、登录成功后token相关处理

@Component
public class JwtTokenService {@AutowiredRedisCache redisCache;//前端请求token对应的header中的keyprivate final static String TOKEN_KEY =  "C-Token";//jwt加密密钥private final static String SIGNING_KEY =  "xice202304181537";//密码过期时间private final static Long EXPIRE_TIME = 30L;//密码过期时间单位private final static TimeUnit EXPIRE_TIME_UNIT = TimeUnit.MINUTES;//token刷新时间间隔private final static Long REFRESH_EXPIRE_TIME = 20 * 60 * 1000L;/*** 登录成功,生成token* @param userInfo* @return*/public String createToken(UserInfo userInfo){//生成uuid,用着redis的key、tokenString uuid = UUID.randomUUID().toString().replaceAll("-", "");//生成tokenMap claims = new HashMap(1);claims.put(TOKEN_KEY,uuid);String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, SIGNING_KEY).compact();//保存tokenuserInfo.setToken(token);//这是过期时间userInfo.setExpireTime(System.currentTimeMillis() + REFRESH_EXPIRE_TIME * 2);//获取权限资源,此处应该查询数据库,方便测试,直接写死Set<String> permissions = new HashSet<>();permissions.add("/user/getUserInfo");userInfo.setPermissions(permissions);//存入redisredisCache.setCacheObjectExpire(uuid,userInfo,EXPIRE_TIME,EXPIRE_TIME_UNIT);return token;}/*** 根据token获取用户信息* @param request* @return*/public UserInfo getUserByToken(HttpServletRequest request){String token = request.getHeader(TOKEN_KEY);Claims claims = Jwts.parser().setSigningKey(SIGNING_KEY).parseClaimsJws(token).getBody();//解密tokenString uuid = (String) claims.get(TOKEN_KEY);//通过uuid获取redis中存的用户信息return redisCache.getCacheObject(uuid);}/*** 刷新token* @param userInfo*/public void refreshToken(UserInfo userInfo){long now = System.currentTimeMillis();//当过期时间与当时时间小于指定的刷新时间间隔是,延长redis中信息的时间if(userInfo.getExpireTime() - now  <= REFRESH_EXPIRE_TIME){System.out.println("刷新token...");redisCache.setCacheObjectExpire(userInfo.getToken(),userInfo,EXPIRE_TIME,EXPIRE_TIME_UNIT);}}
}

4、声明注解,只有加了自定义注解的方法,才进行权限验证

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CAuth {
}

5、使用HandlerInterceptor拦截器进行权限验证,详细方法

@Configuration
public class AuthConfig implements HandlerInterceptor {@AutowiredJwtTokenService tokenService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//静态资源不拦截if(handler instanceof ResourceHttpRequestHandler){return true;}//获取注解Annotation[] annotations = ((HandlerMethod) handler).getMethod().getDeclaredAnnotations();for (Annotation annotation : annotations) {//添加了CAuth注解的方法需要登录后才能访问if(annotation.annotationType().isAssignableFrom(CAuth.class)){//查看是否登录UserInfo loginUser = tokenService.getUserByToken(request);if(ObjectUtils.isEmpty(loginUser)){response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.print("请先登录!");writer.flush();writer.close();return false;}else{//判断是否有权限访问当前资源String requestURI = request.getRequestURI();return loginUser.getPermissions().stream().allMatch(perm -> {if (requestURI.equalsIgnoreCase(perm)) {return true;}response.setContentType("text/html;charset=utf-8");PrintWriter writer = null;try {writer = response.getWriter();writer.print("您没有权限进行此操作!");writer.flush();} catch (IOException e) {e.printStackTrace();} finally {writer.close();}return false;});}}}return true;}}

6、添加拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {@AutowiredAuthConfig authConfig;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authConfig);}
}

7、登录和验证方法

@RestController
@RequestMapping("user")
public class UserInfoController {@AutowiredJwtTokenService jwtTokenService;@RequestMapping("login")public String login(@RequestBody UserInfo userInfo){if("xice".equals(userInfo.getUsername())&&"123456".equals(userInfo.getPassword())){String token = jwtTokenService.createToken(userInfo);return token;}else{return "用户名或密码不正确!";}}@CAuth@RequestMapping("getUserInfo")public UserInfo getUserInfo(HttpServletRequest request){UserInfo userInfo = jwtTokenService.getUserByToken(request);jwtTokenService.refreshToken(userInfo);return userInfo;}@CAuth@RequestMapping("test")public void test(HttpServletRequest request){System.out.println("test。。。");}
}

登录请求 /user/login 没有添加 @CAuth注解,所以不会进行拦截,登录成功后返回一个token值,后续请求需要在header中增加C-Token:token参数

/user/getUserInfo:登陆后有此资源权限,可以获取到当前用户信息

/user/test:无此资源权限,会返回 “您没有权限进行此操作!”