微服务集成sa-token小记

安全框架

想必一旦涉及到Spring安全框架,很容易就会想到SpringSecurityShiro,了解过的都知道,完美的集成一套安全框架是多么麻烦的事情,所以大家都有努力在简化安全框架的集成成本。

我也是在自己做项目中需要集成安全框架才有了这篇文章,上面的两位的集成难度对我这种小菜鸡还是太难了。所以果断寻求别的方案,这不,发现了这个Sa-Token

Sa-Token 介绍

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证单点登录OAuth2.0分布式Session会话微服务网关鉴权 等一系列权限相关问题。

以上介绍复制于官网,在自己体验和使用后,感受就是🥳

Sa-token使用起来真的非常简单方便,具体使用上,直接看官网就好,这里主要讨论一个问题:

在微服务模式下,auth(认证中心)的拆解,是归于system(系统),还是独立的,如果是独立的。。。如果不是独立的。。。

微服务的选择

前面提到了,auth(认证中心)的拆解,是归于system(系统),还是独立的。这个问题的产生还是因为微服务下的鉴权稍稍不太一样,一般的方案都是后端对外提供出网关,再由网关对外部请求进行鉴权,然后转发到对应的微服务,微服务之间有可能存在鉴权,也是需要注意的。

auth独立

auth独立开又可以分为:auth单独查库直接与db交互和借助其他微服务完成数据操作两种。

单独查库

单独查库的好处就是不再限制于微服务之间的鉴权影响,可以自己随意调整auth策略,这也是相对合理些的方案。

借助微服务

authsystem本身上就存在不可避免的联系,auth需要的数据操作存在复用情况,完全可以放在system中,这倒也挑不出毛病。需要注意的就是微服务之前调用的鉴权问题了。

auth不独立

auth不独立的就等同于前面的单独查库的效果了,与system关系更加紧密了,这样来讲其实就只能说是system包含auth,有种名存实亡的感觉了。

我的使用

此仅针对于我的使用理解,案例相对简单,仅供参考。

我使用的是auth独立且借助system的模式,数据操作都放在了system中,auth仅保留一些登录相关的接口,业务逻辑自己实现,借助system微服务提供的数据能力。

网关统一鉴权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
public class AuthFilter {


/**
* 注册 Sa-Token全局过滤器
**/
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦截全部path
.addInclude("/**")
// 开放地址
.addExclude("/favicon.ico")
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**").notMatch("/auth/login", "/auth/register").check(r -> StpUtil.checkLogin());

// 更多匹配 ... */
})
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> SaResult.error("认证失败,无法访问系统资源").setCode(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
}
}

这里开放了/auth/login/auth/register

网关转发鉴权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class ForwardAuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest newRequest = exchange
.getRequest()
.mutate()
// 为请求追加 Same-Token 参数
.header(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken())
.build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
}

/**
* @return -100
*/
@Override
public int getOrder() {
return -100;
}
}

子服务校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@AutoConfiguration
public class SecurityConfiguration implements WebMvcConfigurer {

/**
* 注册sa-token的拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册路由拦截器,自定义验证规则
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}

/**
* 注册 Sa-Token 全局过滤器
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/favicon.ico", "/rpc-api/**")
.setAuth(obj -> {
// 校验 Same-Token 身份凭证 —— 以下两句代码可简化为:SaSameUtil.checkCurrentRequestToken();
SaSameUtil.checkCurrentRequestToken();
})
.setError(e -> SaResult.error("认证失败,无法访问系统资源").setCode(GlobalErrorCodeConstants.UNAUTHORIZED.getCode()));
}
}

前面的是开启权限注解并拦截所有请求,因为对外的只有网关的请求,网关自会转发到对应的微服务。

后面的是子服务校验规则,这里排除了/rpc-api的接口,也就是提供出来的微服务接口不受鉴权限制。

当然这样使用可能存在缺陷,也是在摸索中嘛