# 简单自定义登录 + 自定义授权 + Oauh2单点登录服务整合 **Repository Path**: security-demo/oauth2-client-web-login ## Basic Information - **Project Name**: 简单自定义登录 + 自定义授权 + Oauh2单点登录服务整合 - **Description**: 功能 1. 自定义登录线便于Oauth2登录服务出错时使用 2. 自定义认证校验(授权对象存于redis,实现分布式认证) 3. oauth2授权服务整合 4. oauth2资源服务整合 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-10-22 - **Last Updated**: 2023-01-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 功能 1. 自定义登录线便于Oauth2登录服务出错时使用 1. 自定义认证校验(授权对象存于redis,实现分布式认证) 2. oauth2授权服务整合 3. oauth2资源服务整合 # 架构 1. language:java 1. framework:springframework + projectreactor + thymeleaf + webjars 2. Build Tools:gradle # 启动 1. 拉取oauth2单点代码:https://gitee.com/jdw-silky/silky-sso-server.git 1. 依次启动oauth2授权服务,oauth2资源服务 # 总结————思路篇 ## 自定义登录实现逻辑 1. 配置了自定义登录拦截器, 拦截登录请求生成有效`Authentication`放入`security上下文`,redis保存 `登录标识`——`Authentication` 1. 配置了自定义登录校验拦截器 拦截带有`登录标识`的请求,通过该标识从redis获取`Authentication`,获取不到则代表redis已退出,清理`security上下文` 2. 将这两个拦截器放到security拦截器链路中,FilterChainProxy ## 自定义登录+自定义授权实现逻辑 1. 配置了自定义登录拦截器, 拦截登录请求生成有效`Authentication`放入`security上下文`,redis保存 `登录标识`——`Authentication` 1. 配置了自定义登录校验拦截器 拦截带有`登录标识`的请求,通过该标识从redis获取`Authentication`,获取不到则代表redis已退出,清理`security上下文` 2. 自定义访问决策处理器`AccessDecisionProcessor`,与默认的`WebExpressionVoter`构建访问决策管理器`AccessDecisionManager` 3. 将新构建的访问决策管理器放入security配置中 4. 将这两个拦截器放到security拦截器链路中,FilterChainProxy ## 自定义登录+自定义授权+Oauth2客户端登录授权+Oauth2资源服务实现逻辑 > 需求 > 自定义登录保留 > 未登录访问限制资源默认跳转Oauth2登录页面 > Oauth2登录回来后需要达到与自定义登录一样的效果 ### 登录授权 1. 配置了自定义登录拦截器:拦截自定义登录请求生成有效`Authentication`放入`security上下文`,redis保存 `登录标识`——`Authentication` 1. 配置了自定义登录校验拦截器:拦截带有`登录标识`的请求,通过该标识从redis获取`Authentication`,获取不到则代表redis已退出,清理`security上下文` 2. 自定义访问决策处理器`AccessDecisionProcessor`,与默认的`WebExpressionVoter`构建访问决策管理器`AccessDecisionManager` 3. 将新构建的访问决策管理器放入security配置中 4. 将这两个拦截器放到security拦截器链路中,FilterChainProxy 5. security开始Oauth2登录配置 6. Oauth2登录添加自定义登录成功处理器,开启Oauth2登录 7. 在Oauth2登录成功后,添加自定义登录成功处理器。进行自定义认证逻辑,合并自定义授权与Oauth2授权等操作 ### Oauth2服务整合 1. 配置`spring.security.oauth2.client.provider`地址,该地址拼接`/.well-known/openid-configuration`组成实际Oauth2对接相关信息获取地址 ```json { "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"] } ``` 2. 为向`Oauth2资源服务`发起的请求添加`Oauth2授权`,此处重点就是确保授权码默认认证获得的`Oauth2授权` ```java @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却... ![image-20211022191835119](https://gitee.com/J-dw/learning-document-management/raw/master/picture/ListJiang/image-20211022191835119.png) ![image-20211022191914954](https://gitee.com/J-dw/learning-document-management/raw/master/picture/ListJiang/image-20211022191914954.png) ```sql 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) ); ``` ```java /** * 自定义OAuth2已授权客户端服务 */ @Bean public JdbcOAuth2AuthorizedClientService jdbcOAuth2AuthorizedClientService(JdbcTemplate jdbcTemplate, ClientRegistrationRepository clientRegistrationRepository) { return new JdbcOAuth2AuthorizedClientService(jdbcTemplate, clientRegistrationRepository); } ```