Hello 大家好,这里是Anyin。
在我之前的文章中,不知道大家有没有发现我的代码都是放在Anyin Cloud这个项目的(欢迎大家点个星星)。这个项目我积累了一些我自己平时在工作当中小工具和最佳实践,随着时间的推进这个项目已经慢慢成为一个我个人搭建项目的脚手架,能够快速搭建起来一个完善的基于Spring Cloud技术栈的微服务基础架构。
之前在掘金上看到一个权限认证的框架Sa-Token,简单的了解下,发现确实容易上手,而且功能丰富。今天就让我们来把它集成到 Anyin Cloud项目吧。
在把Sa-Token集成到我们的项目之前,我们需要先梳理下需求,不能为了集成而集成。
Anyin Cloud项目需要一个认证鉴权的框架,经过选型,确定使用Sa-Token。Anyin Cloud项目是一个微服务项目,所以我们统一的认证需要放在认证服务Auth,而统一的鉴权是放在网关Gateway。Auth和Gateway都是高频访问的服务,需要足够轻量,所以我们设计这两个服务都不依赖数据库,并且不会过多依赖其他服务,Auth服务和Gateway服务的数据通信通过Redis。Gateway服务需要把当前登录用户的标识传递到下游。集成认证Auth服务
首先,我们先来处理Auth服务。
添加pom.xml依赖,因为我们需要通过Redis来进行数据通讯,所以需要依赖对应的Redis组件。
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
</dependency>
复制代码
在Auth服务编写Login方法。按我们之前的需求,Auth需要足够轻量,所以它不会去依赖数据库,在登录的时候需要用户信息,通过用户服务upms远程调用从而获取用户信息。
@Component
@Slf4j
public class UsernameLoginHandler implements LoginHandler {
@Autowired
private SysUserFeignApi sysUserFeignApi;
@Override
public LoginTypeEnum extension() {
return LoginTypeEnum.USERNAME;
}
@Override
public LoginUserDTO login(String... content) {
// TODO param check
String username = content[0];
String password = content[1];
SysUserResp sysUser = sysUserFeignApi.infoByUsername(username).getData();
if(sysUser == null){
throw AuthExCodeEnum.USER_NOT_REGISTER.getException();
}
// TODO add salt
if(!sysUser.getPassword().equals(SecureUtil.md5(password))){
throw AuthExCodeEnum.USER_PASSWORD_ERROR.getException();
}
if(UserStatusEnum.DISABLE.getCode().equals(sysUser.getStatus())){
throw AuthExCodeEnum.USER_IS_DISABLE.getException();
}
sysUser.setPassword(null);
StpUtil.login(sysUser.getId());
SaTokenInfo token = StpUtil.getTokenInfo();
LoginUserDTO user = new LoginUserDTO();
user.setSysUser(sysUser);
user.setToken(token);
return user;
}
}
复制代码
根据Sa-Token建议的使用方式,在我们对用户密码进行校验正确之后,通过StpUtil.login来进行框架内部的登录操作,这个操作其实是在Redis上记录对应的信息。在Redis上会记录2个信息:
satoken:login:session:开头,它的值还会包含一些其他的信息satoken:login:token:开头,它的值就是用户ID在登录之后,我们还需要获取token返回给前端,所以这里使用StpUtil.getTokenInfo()获取token信息,最后组装用户信息和token信息返回给前端。

代码编写好,我们需要对登录接口做下测试。

好了,登录的认证我们已经处理好了,简不简单?香不香 ?
集成鉴权Gateway服务
我们接着处理鉴权Gateway服务。老规矩,先添加依赖。
这里要特别注意了,因为我们的网关是Spring Cloud Gateway,底层是WebFlux实现,它是基于Reactor模型编程的;而之前的Auth服务是正常的SpringMVC服务,基于Servlet模型编程的。
所以我们这里引入的是
sa-token-reactor-spring-boot-starter
<!-- sa-token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
</dependency>
复制代码
接着,我们继续编写Gateway服务的过滤器,在过滤器中我们主要做以下3个事情:
对于判断哪些路由需要鉴权,我们可以在动态路由中配置路由的元数据,从而判断当前路由是否鉴权。代码如下:
Route route = (Route)exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
Integer needAuth =(Integer) route.getMetadata().get(GatewayConstants.SYS_ROUTE_AUTH_KEY);
// 无需认证的路由
if(!NEED_AUTH.equals(needAuth)){
return chain.filter(exchange);
}
复制代码
当路由需要进行鉴权的时候,我们再使用Sa-Token提供的isLogin方法进行判断,如果未登录则响应异常信息。如下:
// 判断是否登录
if(!StpUtil.isLogin()){
return this.response(exchange, CommonExCodeEnum.USER_NOT_LOGIN.getException());
}
复制代码
如果当前请求用户已经登录,则使用StpUtil.getLoginId方法获取当前用户ID,然后透传到下游服务,如下:
private ServerWebExchange setHeaderLoginId(ServerWebExchange exchange, String loginId){
ServerHttpRequest request = exchange.getRequest().mutate().header(CommonConstants.USER_ID, loginId).build();
return exchange.mutate().request(request).build();
}
复制代码
最后,很关键的一步,我们还需要注册全局的过滤器,除了我们自己编写的过滤器,还有Sa-Token的过滤器。如果细心的同学可以发现StpUtil工具类提供的方法都是很简单,得益于它需要注册一个全局的过滤器SaReactorFilter,通过该过滤器它把大量的上下文信息和对应的逻辑都在内部处理掉。所以,我们需要注册2个过滤器,如下:
@Bean
public GatewayAuthFilter gatewayAuthFilter(){
return new GatewayAuthFilter();
}
@Bean
public SaReactorFilter saReactorFilter(){
return new SaReactorFilter();
}
复制代码
其实SaReactorFilter过滤器提供了很多的方法和参数,用来处理各种业务场景,但是因为我这边可能需要对过滤器进行更加定制化的逻辑处理,所以未使用它内部的一些方法。
测试
完成了以上2部分代码,我们需要对其进行测试下,看看框架是否好事。
首先,测试下不传递token的场景,是否会报未登录的异常。

传递token的场景下,能够正常返回信息

感谢Sa-Token作者提供了这么一个牛皮的框架。其文档地址 Sa-Token。
以上,就是Spring Cloud Gateway集成Sa-Token的步骤,如果有什么问题,欢迎指正。
后面会深入Sa-Token源码,了解更多的使用方法和设计思想,敬请期待。
似乎无法为此找到有效的答案。我正在阅读Rails教程的第10章第10.1.2节,但似乎无法使邮件程序预览正常工作。我发现处理错误的所有答案都与教程的不同部分相关,我假设我犯的错误正盯着我的脸。我已经完成并将教程中的代码复制/粘贴到相关文件中,但到目前为止,我还看不出我输入的内容与教程中的内容有什么区别。到目前为止,建议是在函数定义中添加或删除参数user,但这并没有解决问题。触发错误的url是http://localhost:3000/rails/mailers/user_mailer/account_activation.http://localhost:3000/rails/mai
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon
我试图在我的网站上实现使用Facebook登录功能,但在尝试从Facebook取回访问token时遇到障碍。这是我的代码:ifparams[:error_reason]=="user_denied"thenflash[:error]="TologinwithFacebook,youmustclick'Allow'toletthesiteaccessyourinformation"redirect_to:loginelsifparams[:code]thentoken_uri=URI.parse("https://graph.facebook.com/oauth/access_token
我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow
简单代码require'net/http'url=URI.parse('getjson/otherdatahere[link]')req=Net::HTTP::Get.new(url.to_s)res=Net::HTTP.start(url.host,url.port){|http|http.request(req)}putsres.body只是想知道如何在phpcURL中放置身份验证token,我是这样做的 curl_setopt($ch,CURLOPT_HTTPHEADER,array('Authorization:Bearerxxx'));//Bearertokenfora
Doorkeeper中Token和Grant的区别我搞不清楚。Doorkeeper在哪个时刻创建访问授权,何时创建访问token?文档似乎对此什么也没说,现在我正在阅读代码,但不是十几行。 最佳答案 我还建议阅读documentationofoauth2据我了解,Doorkeeper也是基于该文档中描述的协议(protocol)。在doorkeeper中,你会先获得accessgrant,然后是accesstoken。访问授权通常只存在很短的时间(doorkeeper中的默认值为10分钟)。您将通过向api-url/oauth/au
三分钟集成Tap防沉迷SDK(Unity版)一、SDK介绍基于国家对上线所有游戏必须增加防沉迷功能的政策下,TapTap推出防沉迷SDK,供游戏开发者进行接入;允许未成年用户在周五、六、日以及法定节假日晚上8:00-9:00进行游戏,防沉谜时间段进入游戏会弹窗进行提示!开发环境要求:Unity2019.4或更高版本iOS10或更高版本Android5.0(APIlevel21)或更高版本🔗Unity集成Demo参考链接🔗UnityTapSDK功能体验APK下载链接二、集成前准备1.创建应用进入开发者后台,按照提示开始创建应用;2.开通服务在使用TDS实名认证和防沉迷服务之前,需要在上面创建的应