Security对接 sso服务总结——思路篇
功能
- 自定义登录线便于Oauth2登录服务出错时使用
- 自定义认证校验(授权对象存于redis,实现分布式认证)
- oauth2授权服务整合
- oauth2资源服务整合
代码地址
- sso服务:https://gitee.com/jdw-silky/silky-sso-server.git
- client-web:https://gitee.com/security-demo/oauth2-client-web-login.git
架构
- language:java
- framework:springframework + projectreactor + thymeleaf + webjars
- Build Tools:gradle
启动
- 拉取oauth2单点代码:https://gitee.com/jdw-silky/silky-sso-server.git
- 依次启动oauth2授权服务,oauth2资源服务
总结————思路篇
自定义登录实现逻辑
-
配置了自定义登录拦截器,
拦截登录请求生成有效Authentication
放入security上下文
,redis保存登录标识
——Authentication
-
配置了自定义登录校验拦截器
拦截带有登录标识
的请求,通过该标识从redis获取Authentication
,获取不到则代表redis已退出,清理security上下文
-
将这两个拦截器放到security拦截器链路中,FilterChainProxy
自定义登录+自定义授权实现逻辑
- 配置了自定义登录拦截器,
拦截登录请求生成有效Authentication
放入security上下文
,redis保存登录标识
——Authentication
- 配置了自定义登录校验拦截器
拦截带有登录标识
的请求,通过该标识从redis获取Authentication
,获取不到则代表redis已退出,清理security上下文
- 自定义访问决策处理器
AccessDecisionProcessor
,与默认的WebExpressionVoter
构建访问决策管理器AccessDecisionManager
- 将新构建的访问决策管理器放入security配置中
- 将这两个拦截器放到security拦截器链路中,FilterChainProxy
自定义登录+自定义授权+Oauth2客户端登录授权+Oauth2资源服务实现逻辑
需求
自定义登录保留
未登录访问限制资源默认跳转Oauth2登录页面
Oauth2登录回来后需要达到与自定义登录一样的效果
登录授权
- 配置了自定义登录拦截器:拦截自定义登录请求生成有效
Authentication
放入security上下文
,redis保存登录标识
——Authentication
- 配置了自定义登录校验拦截器:拦截带有
登录标识
的请求,通过该标识从redis获取Authentication
,获取不到则代表redis已退出,清理security上下文
- 自定义访问决策处理器
AccessDecisionProcessor
,与默认的WebExpressionVoter
构建访问决策管理器AccessDecisionManager
- 将新构建的访问决策管理器放入security配置中
- 将这两个拦截器放到security拦截器链路中,FilterChainProxy
- security开始Oauth2登录配置
- Oauth2登录添加自定义登录成功处理器,开启Oauth2登录
- 在Oauth2登录成功后,添加自定义登录成功处理器。进行自定义认证逻辑,合并自定义授权与Oauth2授权等操作
Oauth2服务整合
- 配置
spring.security.oauth2.client.provider
地址,该地址拼接/.well-known/openid-configuration
组成实际Oauth2对接相关信息获取地址
{
"issuer": "http://192.168.137.139:9000",
"authorization_endpoint": "http://192.168.137.139:9000/oauth2/authorize",
"token_endpoint": "http://192.168.137.139:9000/oauth2/token",
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"jwks_uri": "http://192.168.137.139:9000/oauth2/jwks",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid"]
}
- 为向
Oauth2资源服务
发起的请求添加Oauth2授权
,此处重点就是确保授权码默认认证获得的Oauth2授权
@GetMapping(value = "/authorize", params = "grant_type=authorization_code")
public String authorizationCodeGrant(Model model,
@RegisteredOAuth2AuthorizedClient("messaging-client-authorization-code")
OAuth2AuthorizedClient authorizedClient) {
String[] messages = this.webClient
.get()
.uri(this.messagesBaseUri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String[].class)
.block();
model.addAttribute("messages", messages);
return "index";
}
@GetMapping(value = "/authorize", params = "grant_type=client_credentials")
public String clientCredentialsGrant(Model model) {
String[] messages = this.webClient
.get()
.uri(this.messagesBaseUri)
.attributes(clientRegistrationId("messaging-client-client-credentials"))
.retrieve()
.bodyToMono(String[].class)
.block();
model.addAttribute("messages", messages);
return "index";
}
重点
当使用oauth2登录的时候,redis里面修改为存oauth2认证成功的
OAuth2AuthenticationToken
,
不然会导致确实oauth2登录状态丢失,后面无法退出;无法获取oauth2资源服务的资源
注意点
问题:项目重启后,使用旧有存于redis的自定义token。无法访问Oauth2资源,可以访问本系统资源
原因:我们采用InMemoryOAuth2AuthorizedClientService
保存Oauth2的已登录客户端,当我们服务器访问oauth2资源的时候需要通过当前token解析获得的Oauth-registerId来获取该客户端。
解决方案:使用JdbcOAuth2AuthorizedClientService
吐槽:该表的创建sql,用百度搜索是想都不要想,不可能找得到的。然而google却...
CREATE TABLE oauth2_authorized_client (
client_registration_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
access_token_type varchar(100) NOT NULL,
access_token_value blob NOT NULL,
access_token_issued_at timestamp NOT NULL,
access_token_expires_at timestamp NOT NULL,
access_token_scopes varchar(1000) DEFAULT NULL,
refresh_token_value blob DEFAULT NULL,
refresh_token_issued_at timestamp DEFAULT NULL,
created_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (client_registration_id, principal_name)
);
/**
* 自定义OAuth2已授权客户端服务
*/
@Bean
public JdbcOAuth2AuthorizedClientService jdbcOAuth2AuthorizedClientService(JdbcTemplate jdbcTemplate,
ClientRegistrationRepository clientRegistrationRepository) {
return new JdbcOAuth2AuthorizedClientService(jdbcTemplate, clientRegistrationRepository);
}