说明

SpringSecurity接入Oauth2认证有两种方式,一种是SpringCloud推荐的依赖org.springframework.cloud:spring-cloud-starter-oauth2+org.springframework.cloud:spring-cloud-starter-security,另一种是不依赖SpringCloud的org.springframework.security:spring-security-oauth2-client

目前主要想实现一套单点登录的统一逻辑,也没有依赖到SpringCloud,因此采用第二种实现org.springframework.security:spring-security-oauth2-client
需要注意的是,Oauth2原来还有org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigureorg.springframework.security.oauth:spring-security-oauth2这两个是历史版本的,已经废弃了

依赖

本次主要依赖SpringSecurity 5.3的版本,因为自己项目springboot版本问题,建议依赖5.7版本,这个更新,当然,相关的配置肯定会有稍许调整

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>5.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.kewen.framework.auth</groupId>
<artifactId>auth-spring-boot-starter</artifactId>
</dependency>

Java配置

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@Configuration
@EnableConfigurationProperties(Oauth2Properties.class)
public class Oauth2Config extends WebSecurityConfigurerAdapter {

@Autowired
private SecurityAuthenticationSuccessHandler successHandler;

@Autowired
private SecurityAuthenticationExceptionResolverHandler exceptionResolverHandler;

@Autowired
private Oauth2Properties oauth2Properties;

@Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
@Bean
public Oauth2AuthenticationSuccessResultConverter oauth2JsonSuccessResultConverter(){
return new Oauth2AuthenticationSuccessResultConverter();
}
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
return new DefaultAuthorizationCodeTokenResponseClient();
}

/**
* 这里自定义了一个DefaultOAuth2UserService,用来解决Oauth2默认的ssl证书校验,如果不需要的话就不用此处的Bean,会默认生成一个
* @return
*/
@Bean
public OAuth2UserService oAuth2UserService() {
//return new IdaasSPOAuth2UserService();
DefaultOAuth2UserService defaultOAuth2UserService = new DefaultOAuth2UserService();
defaultOAuth2UserService.setRestOperations(new RestOperationBuilder().enableSsl(false).build());
return defaultOAuth2UserService;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
ClientRegistrationRepository clientRegistrationRepository = clientRegistrationRepository();
http.oauth2Login(oauth2 -> {
oauth2
// 配置成功处理器,和项目中统一就行
.successHandler(successHandler)
// 配置失败处理器,和项目中统计就行
.failureHandler(exceptionResolverHandler)
// 配置授权端点
.authorizationEndpoint(endpoint -> {
endpoint.baseUri(oauth2Properties.getAuthorizationUri())
//.authorizationRequestRepository(authorizationRequestRepository())
//.authorizationRequestResolver(new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, oauth2Properties.getAuthorizationUri()));
;
})
// 配置令牌端点
.tokenEndpoint(endpoint -> {
endpoint.accessTokenResponseClient(accessTokenResponseClient());
})
.userInfoEndpoint(endpoint -> {
endpoint.userService(oAuth2UserService());
})
.loginProcessingUrl(oauth2Properties.getLoginProcessingUrl())
.clientRegistrationRepository(clientRegistrationRepository)
;
});
}
private ClientRegistrationRepository clientRegistrationRepository(){
List<ClientRegistration> registrationList = getClientRegistrations();
return new InMemoryClientRegistrationRepository(registrationList);
}



private List<ClientRegistration> getClientRegistrations() {
List<ClientRegistration> registrationList = oauth2Properties.getClients().stream().map(
client -> {
ClientRegistration.Builder builder = ClientRegistration.withRegistrationId(client.getRegistrationId());
return builder.registrationId(client.getRegistrationId()).clientId(client.getClientId())
.clientSecret(client.getClientSecret())
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
.authorizationUri(client.getAuthorizationUri())
.userInfoUri(client.getUserInfoUri())
.userNameAttributeName(client.getUserNameAttributeName())
.scope(client.getScopes())
.tokenUri(client.getTokenUri())
.build();
}
).collect(Collectors.toList());
return registrationList;
}
}

主要配置在configure方法中,最小需要配置授权端点.authorizationEndpoint(),令牌端点.tokenEndpoint(),用户信息端点.userInfoEndpoint(),client存储的相关信息.clientRegistrationRepository()否则无法启动;

涉及到的登录端点有两个

  • 默认的sp登录发起地址/oauth2/authorize
  • IDP授权码回调的地址{baseUrl}/login/oauth2/code/{registrationId}

这两个地址按需修改,同样,可以根据模板来自定义回调地址

注意

这里需要注意的是这里Oauth2认证完成之后,在OAuth2LoginAuthenticationFilter中会直接构造一个OAuth2AuthenticationToken并返回,导致后续添加到Session中的对象只能是OAuth2AuthenticationToken,这个即使换了AuthticationProvider也没有办法处理,除非自定义Filter修改代码,但这又是SpringSecurity不推荐的,会破坏其共享对象的结构,导致后续出现一系列问题,如sesison配置问题,rememberme配置问题等
后续版本不知道SpringSecurity会留出这个扩展没有。