Springboot中权限认证的基本方案:
可以对URL进行HandlerInterceptor拦截,然后权限验证。
Client登录成功后,每次发送请求时,会将token等信息存放到header中。
Server收到请求,在HandlerInterceptor中从header获取用户的信息(userId,token等),然后存入上下文context;在后面Controller中就可以从上下文context中获取用户的信息做权限认证。
-
单机应用上下文context:
Threadlocal,在进程内部,我们可以使用ThreadLocal传递应用上下文的方式.。 -
分布式系统上下文context:
需要借助以下方式实现:
1)数据库; 2)Redis ;3)文件系统。
1 HandlerInterceptor 拦截ThreadLocal实现上下文
这里讲一下单机应用Threadlocal。
ThreadLocal不是线程,是线程变量;.ThreadLocal 是 Java中一种较为特殊的线程绑定机制.通过ThreadLocal存取的数据,总是与当前线程相关。
第1步:创建User对象:
public class User {
public String userId;
public String orgId;
public String token;
}
第2步:创建ThreadLocal对象来存取User:
public class ContextManager {
private static final ThreadLocal<User> threadLocal= new ThreadLocal();
public static User getUser() {
return threadLocal.get();
}
public static void addUser(User user){
threadLocal.set(user);
}
public static void removeUser(){
threadLocal.remove();
}
public static String getUserId(){
return threadLocal.get().userId;
}
public static String getToken(){
return threadLocal.get().token;
}
}
第3步:创建HandlerInterceptor:
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private HttpRequest httpRequest;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("threadId:"+Thread.currentThread().getId());
String userId = (String)request.getHeader("userId");
String token = (String)request.getHeader("token");
if(!StringUtils.hasLength(userId)){
response.getWriter().println("please login");
return false;
}
if(!StringUtils.hasLength(token)){
response.getWriter().println("please login");
return false;
}
//将用户相关的信息存入threadlocal中
User user = new User();
user.userId = userId;
user.token = token;
ContextManager.addUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion:"+Thread.currentThread().getId());
ContextManager.removeUser();
}
}
第4步:创建WebMvcConfigurer:
Spring Boot 1.5版本:重写WebMvcConfigurerAdapter的方法来添加自定义拦截器。
SpringBoot 2.0 版本后,该类被标记为@Deprecated(弃用)。官方推荐直接实现WebMvcConfigurer或者直接继承WebMvcConfigurationSupport,
1)实现WebMvcConfigurer接口(推荐),
2)继承WebMvcConfigurationSupport类
@Configuration
public class LWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
ArrayList<String> whiteList = new ArrayList<>();
whiteList.add("/login");
whiteList.add("/logout");
registry.addInterceptor(new AuthInterceptor())
.excludePathPatterns(whiteList)
.addPathPatterns("/**");
}
}
第5步:创建HelloController 来验证从threadlocal中获取数据:
@RestController
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
String token = ContextManager.getToken();
if(!StringUtils.hasLength(token)){
return "please login!";
}
log.info("token:"+ token);
String userId = ContextManager.getUserId();
if(!StringUtils.hasLength(userId)){
return "please login!";
}
log.info("userId:"+ userId);
return "hello world!";
}
}
2 测试验证
2.1 请求有token
controller中获取token:
2.2 请求没有token
需要用户的请求header中增加token。