环境:springboot2.3.10.RELEASE + OAuth2
请先阅读《SpringBoot2 整合OAuth2实现统一认证 》文章,本篇内容是调用之前写的一个OAuth2认证服务。
相关依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
应用配置
server:servlet:session:timeout: 30m
---
spring:security:oauth2:client:provider:xgpack:authorization-uri: http://localhost:8208/oauth/authorizetoken-uri: http://localhost:8208/oauth/tokenuser-info-uri: http://localhost:8208/users/userinfouser-name-attribute: nameregistration:auth2:provider: xgpackclient-id: 1client-secret: 1authorization-grant-type: authorization_coderedirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
---
logging:level:org.springframework.security: debug
说明:
authorization-uri:你的认证服务地址
token-uri:获取token地址
user-info-uri:获取用户信息地址
user-name-attribute:用户名,在获取用户信息接口返回的json中的key
redirect-uri:跳转地址,这个地址必须与服务认证那里配置的跳转地址一致。{baseUrl}系统会自动替换你当前服务的地址及端口,{registrationId} 会被替换成auth2。默认情况下系统只能处理/login/oauth2/code/* 地址,当登录成功后跳转回来这个地址时由
OAuth2LoginAuthenticationFilter过滤器进行处理。部分源码如下:
public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";private static final String CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE = "client_registration_not_found";private ClientRegistrationRepository clientRegistrationRepository;private OAuth2AuthorizedClientRepository authorizedClientRepository;private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =new HttpSessionOAuth2AuthorizationRequestRepository();public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository,OAuth2AuthorizedClientService authorizedClientService) {this(clientRegistrationRepository, authorizedClientService, DEFAULT_FILTER_PROCESSES_URI);}// ....
}
DEFAULT_FILTER_PROCESSES_URI:默认该过滤器能处理的请求地址。该过滤器的作用就是获取token信息然后交由AuthenticationManager以登录最终用户。
Security配置
@Configuration
public class OAuthConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable() ;http.authorizeRequests().antMatchers("/error", "/webjars/**", "/resources/**", "/index/**").permitAll().anyRequest().authenticated().and().oauth2Login().logout();.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);}
}
这里设置了下session,因为不设置在跟踪源码的时候你会发现session始终为空
到此配置就完成了,接下来进行测试。
访问测试接口/home
登录后出现如下错误信息
这个错误的原因就是从session中无法获取
OAuth2AuthorizationRequest保存的对象。
OAuth2LoginAuthenticationFilter.java
跟踪源码查看问题出在哪里?
进入
OAuth2LoginAuthenticationFilter类
进入选中的方法中
注意这里的
Map<String, OAuth2AuthorizationRequest> authorizationRequests = session == null ? null : (Map<String, OAuth2AuthorizationRequest>) session.getAttribute(this.sessionAttributeName);
这行代码是从session中获取跳转前保存在session中的
OAuth2AuthorizationRequest对象,没有被获取
在整个处理流中并不能从session中获取信息
OAuth2AuthorizationRequest信息。那这个对象又是在哪里设置的呢?是由另外一个核心Filter处理的
OAuth2AuthorizationRequestRedirectFilter。
注意:这里有两次的重定向,首次这里的if肯定是不会进入的,因为该过滤器默认处理的请求地址是/oauth2/authorization
public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
}
第一次重定向:
接着
OAuth2AuthorizationRequestRedirectFilter就能处理该请求,并且进入if语句中进行重定向到认证服务。而设置保存
OAuth2AuthorizationRequest对象到session中就在如下方法中。
this.sendRedirectForAuthorization(request, response, authorizationRequest);
保存OAuth2AuthorizationReques对象到session中
从session获取OAuth2AuthorizationReques对象
设置和获取使用的key都是同一个(确保没有错误)
测试session是否真的有东西?我把跳转到认证服务的地址改错,然后请求,接着用另外一个接口打印session中的内容。
session中是有内容的,接着吧跳转地址改对,再看session中的内容
跳转到登录页面后,不进行登录,接着访问打印session的接口:
session中已经没有内容了。
证实:session是跳转到了认证服务后就没有了。难道真的是stackoverflow上所说?
翻译过来就是:
这些错误意味着找不到授权请求.授权请求存储在会话中,因此有些会话未被存储.默认情况下,会话由cookie管理.
所以我认为这可能是因为你在localhost上运行了所有东西,所以第一个cookie由localhost:8080设置来存储授权请求会话数据,&当你登录到localhost:8081时,它会为它的会话设置另一个cookie。(这里经查,cookie同ip不同端口,cookie共享)。
最后来个结果图,能够返回code,有了这个就可以获取token,获取了token就可以获取用户信息,然后将用户信息交由AuthenticationManager管理实现登录。
解决上面的问题
在c:\windows\System32\drivers\etc\host文件中添加了如下
让我们的认证服务通过这个域名访问,修改应用配置文件:
spring:security:oauth2:client:provider:xgpack:authorization-uri: http://www.xg.com:8208/oauth/authorizetoken-uri: http://www.xg.com:8208/oauth/tokenuser-info-uri: http://www.xg.com:8208/users/userinfouser-name-attribute: nameregistration:auth2:provider: xgpackclient-id: 1client-secret: 1authorization-grant-type: authorization_coderedirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
都修改域名形式,这样我们的cookie应该不会被替换了。接下来再次测试,果然成功了。这里的处理流程是:获取token,获取token后调用获取用户信息接口。
1、自动调用获取token的接口
OAuth2LoginAuthenticationProvider.java
2、通过上一步获取的token再自动调用获取用户信息表
注意:获取用户信息的接口,你应该只验证token是否合法即可,不要用security再进行拦截了。security应该排除这个接口。接口如下:
@GetMapping("/userinfo")
public Map<String, Object> userinfo(){ Map<String, Object> res = new HashMap<>() ;String token = extractToken() ;OAuth2Authentication auth = tokenService.loadAuthentication(token) ;res.put("name", auth.getName()) ;return res ;
}
整合第三方OAuth2的核心过滤器:
OAuth2AuthorizationRequestRedirectFilter.java
OAuth2LoginAuthenticationFilter.java
完毕!!!
给个关注吧谢谢
SpringBoot2 整合 OAuth2 资源认证(保护)
SpringBoot2 整合OAuth2实现统一认证
SpringBoot2 整合OAuth2自定义登录与授权页面
ThreadPoolExecutor 参数说明
Springboot之Actuator详解
SpringBoot配置HTTPS支持 单向认证
Tomcat配置参数connectionTimeout意义
SpringCloud Hystrix实现资源隔离应用
Spring Boot Security防重登录及在线总数
Springboot Security 基础应用 (1)
Spring Cloud Sentinel 熔断降级
Spring Cloud Nacos 开启权限验证
Spring Cloud Sentinel 流控限流
Spring Cloud Gateway应用详解1之谓词
SpringCloud zuul 动态网关配置