思路
- 用户登录去校验用户信息,校验成功后将登录后的信息持久化,并返回一个系统可识别的信息串
- 用户每次访问接口将参数和信息串一起访问,然后通过拦截进行对信息串统一识别认证
实践
用户登录
通过用户名和密码验证是否登录成功,我这里是用accessToken作为系统可识别的信息串,持久化你可以存入数据库,也可以放在redis,但是经常要验证accessToken,建议放在redis中好一点。
@ApiOperation("登录")
@PostMapping("login")
@AuthLogin(needLogin = false)
public RestResponse<User> login(String username,String password){
return RestResponse.ok(userService.login(username,password));
}
@Override
public UserVO login(String username, String password) {
User user = new User() ;
user.setUsername(username);
user.setPassword(password);
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(user);
User users = this.getOne(userQueryWrapper);
if(users == null ){
throw new BusinessException("用户名或者密码错误");
}
//生成accessToken
String token = tokenService.createToken(users.getId());
users.setAccessToken(token);
//登录成功后返回用户信息和accessToken
return users.of();
}
accessToken拦截和验证
accessToken拦截是通过拦截器进行拦截,其中包括哪些接口需要拦截,这里也会用到自定义注解,识别和使用,其次也会增加一些拦截器。
1.创建拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//全局登录拦截请求
registry.addInterceptor(new AuthLoginIntercept()).addPathPatterns("/**")
.excludePathPatterns("/file/**")
.excludePathPatterns("/login/**")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
.excludePathPatterns("/druid/**");
}
/**
* 上传文件外部访问拦截
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/file/**")
.addResourceLocations("file:"+BaseUploadPath);
}
}
2.自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Documented
@Inherited
public @interface AuthLogin {
/**
*是否需要登录
* 默认:需要登录
*/
boolean needLogin() default true ;
}
3.拦截器实现方法
@Service
public class AuthLoginIntercept extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
//拦截所有的请求方法,是否需要登录
AuthLogin authLoginMethod = handlerMethod.getMethod().getAnnotation(AuthLogin.class);
if(authLoginMethod != null && !authLoginMethod.needLogin()){
return true;
}
//拦截整个模块,是否需要登录
AuthLogin authLoginClass = handlerMethod.getBeanType().getAnnotation(AuthLogin.class);
if(authLoginClass != null && !authLoginClass.needLogin()){
return true;
}
//验证token信息
this.validAuth();
}
return true;
}
/**
* 验证token信息
*/
private void validAuth(){
String token = RequestUtil.getToken();
TokenService tokenService = SpringUtil.getBean(TokenService.class);
//验证token是否符合条件
tokenService.validateToken(token);
}
}
4.验证token
@Override
public UserVO validateToken(String accessToken) {
//通过解析token
UserVO user = getUser(accessToken);
//更新用户当前的最新信息
User updateUser = new User();
updateUser.setId(user.getId());
updateUser.setLastLoginTime(new Date());
userService.update(updateUser);
return user;
}
通过token获取用户信息
/**
* 获取用户信息
* @param accessToken
* @return
* @throws BusinessException
*/
public UserVO getUser(String accessToken)throws BusinessException {
//验证token是否正确
boolean validToken = validToken(accessToken);
if(validToken){
//解密token信息
UserVO user = decodeUser(accessToken);
validUser(user);
return user;
}
return null;
}
验证token的合法性
/**
* 校验token信息
* @param accessToken 认证
* @return
* @throws BusinessException
*/
public boolean validToken(String accessToken) throws BusinessException{
if(StrUtil.isBlank(accessToken)){
throw new BusinessException(Login.getCode(),NotLogin.getCode(),NotLogin.getMsg());
}
boolean keyExists = redisService.isKeyExists(AccessToken.getKey(), accessToken);
if(!keyExists) {
throw new BusinessException(Login.getCode(),TokenLose.getCode(), TokenLose.getMsg());
}
return true;
}
我这里是用的base64加密用户信息
/**
* 解密用户信息
* @param accessToken
* @return
*/
public UserVO decodeUser(String accessToken){
String userString= (String) redisService.get(AccessToken.getKey(), accessToken);
String userJson = Base64.decodeStr(userString);
return JSONObject.parseObject(userJson,UserVO.class);
}
maven Jar包依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 缓存依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>