Spring Boot整合Spring Security简记-O
new無语 转载请注明原创出处,谢谢!
HttpSecurity.oauth2Login()
提供了许多用于自定义OAuth 2.0登录的配置选项。主要配置选项在他们的Endpoint()
中。
例如,oauth2Login().authorizationEndpoint()
允许配置授权端点,而oauth2Login().tokenEndpoint()
允许配置令牌端点。
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.authorizationEndpoint()
...
.redirectionEndpoint()
...
.tokenEndpoint()
...
.userInfoEndpoint()
...
}
}
OAuth 2.0授权框架将协议端点定义如下:
授权过程使用两个授权服务器端点(HTTP资源):
- Authorization Endpoint(授权端点):客户端通过用户代理重定向从资源所有者获取授权。
- Token Endpoint(令牌端点):客户端用来为访问令牌交换授权授权,通常使用客户端验证。
以及一个客户端端点: - Redirection Endpoint(重定向端点):由授权服务器用来通过资源所有者用户代理将包含授权凭证的响应返回给客户端。
OpenID Connect Core 1.0规范定义了UserInfo端点,如下所示:
UserInfo端点是一个OAuth 2.0保护资源,用于返回有关经过身份验证的最终用户的信息。为了获得有关最终用户的请求信息,客户端使用通过OpenID Connect Authentication获取的访问令牌向UserInfo端点发出请求。
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.clientRegistrationRepository(this.clientRegistrationRepository())
.authorizedClientService(this.authorizedClientService())
.loginPage("/login")
.authorizationEndpoint()
.baseUri(this.authorizationRequestBaseUri())
.authorizationRequestRepository(this.authorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri(this.authorizationResponseBaseUri())
.and()
.tokenEndpoint()
.accessTokenResponseClient(this.accessTokenResponseClient())
.and()
.userInfoEndpoint()
.userAuthoritiesMapper(this.userAuthoritiesMapper())
.userService(this.oauth2UserService())
.oidcUserService(this.oidcUserService())
.customUserType(GitHubOAuth2User.class, "github");
}
}
以下将详细介绍每个可用的配置选项:
OAuth 2.0登录页面
默认情况下,OAuth 2.0登录页面是由DefaultLoginPageGeneratingFilter
自动生成的。默认登录页面显示每个已配置的OAuth客户端ClientRegistration.clientName
作为链接,可以启动授权请求(或OAuth 2.0登录)。
每个OAuth客户端的链接目标默认为以下内容:
OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
+/ {registrationId}
<a HREF = "/oauth2/authorization/google">谷歌</a>
要覆盖默认登录页面,请配置oauth2Login().loginPage()
和(可选)oauth2Login().authorizationEndpoint().baseUri()
。
例子:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.loginPage("/login/oauth2")
...
.authorizationEndpoint()
.baseUri("/login/oauth2/authorization")
....
}
}
oauth2Login().loginPage()
需要自己实现登陆页面。
Authorization Endpoint(授权端点)
AuthorizationRequestRepository
负责保存维护OAuth2AuthorizationRequest
。
OAuth2AuthorizationRequest
被用来关联和验证授权响应。
AuthorizationRequestRepository
的默认实现是HttpSessionOAuth2AuthorizationRequestRepository
将OAuth2AuthorizationRequest
在HttpSession
中存储。
如果想提供一个自定义实现AuthorizationRequestRepository
存储OAuth2AuthorizationRequest
在Cookie
。例子:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(this.cookieAuthorizationRequestRepository())
...
}
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
}
还需要确保
ClientRegistration.redirectUriTemplate
与自定义授权响应相匹配baseUri
。return CommonOAuth2Provider.GOOGLE.getBuilder("google") .clientId("google-client-id") .clientSecret("google-client-secret") .redirectUriTemplate("{baseUrl}/login/oauth2/callback/{registrationId}") .build();
Token Endpoint(令牌端点)
- OAuth2AccessTokenResponseClient
OAuth2AccessTokenResponseClient
负责在授权服务器的令牌端点交换访问令牌凭证的授权许可证书。
OAuth2AccessTokenResponseClient
的缺省实现是NimbusAuthorizationCodeTokenResponseClient
在令牌端点交换访问令牌的授权码。
如果想提供一个OAuth2AccessTokenResponseClient
使用Spring Framework 5 的自定义实现WebClient
来启动对令牌端点的请求,请按照以下示例中所示进行配置:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.tokenEndpoint()
.accessTokenResponseClient(this.accessTokenResponseClient())
...
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
return new SpringWebClientAuthorizationCodeTokenResponseClient();
}
}
UserInfo Endpoint(UserInfo端点)
映射用户权限
用户成功通过OAuth 2.0服务程序进行身份验证后,OAuth2User.getAuthorities()
(或OidcUser.getAuthorities()
)可能会映射到一组新的GrantedAuthority
实例,这些实例将OAuth2AuthenticationToken
在完成身份验证时提供给该实例。
OAuth2AuthenticationToken.getAuthorities()
用于授权请求,如in hasRole('USER')
或hasRole('ADMIN')
。
映射用户权限时有几个选项可供选择:
- 用一个
GrantedAuthoritiesMapper
- 基于代理的策略
OAuth2UserService
用一个 GrantedAuthoritiesMapper
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.userInfoEndpoint()
.userAuthoritiesMapper(this.userAuthoritiesMapper())
...
}
private GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;
OidcIdToken idToken = oidcUserAuthority.getIdToken();
OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();
// Map the claims found in idToken and/or userInfo
// to one or more GrantedAuthority's and add it to mappedAuthorities
} else if (OAuth2UserAuthority.class.isInstance(authority)) {
OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;
Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
// Map the attributes found in userAttributes
// to one or more GrantedAuthority's and add it to mappedAuthorities
}
});
return mappedAuthorities;
};
}
}
或者直接注册@Bean
自动配置
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Login();
}
@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
...
}
}
基于代理的策略 OAuth2UserService
它允许您访问的OAuth2UserRequest
和OAuth2User
(使用OAuth 2.0用户UserService
时)或OidcUserRequest
和OidcUser
(使用 OpenID Connect 1.0 UserService时)。
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(this.oidcUserService())
...
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
OAuth2AccessToken accessToken = userRequest.getAccessToken();
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
// TODO
// 1) Fetch the authority information from the protected resource using accessToken
// 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
// 3) Create a copy of oidcUser but use the mappedAuthorities instead
oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
return oidcUser;
};
}
}
- 配置自定义OAuth2User
CustomUserTypesOAuth2UserService
是OAuth2UserService
为自定义OAuth2User
类型提供支持的实现。
如果默认实现(DefaultOAuth2User
)不适合你的需要,你可以定义你自己的实现OAuth2User
。
以下代码演示了如何OAuth2User
为GitHub 注册一个自定义类型:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.userInfoEndpoint()
.customUserType(GitHubOAuth2User.class, "github")
...
}
}
以下代码显示了OAuth2UserGitHub
的自定义类型的示例:
public class GitHubOAuth2User implements OAuth2User {
private List<GrantedAuthority> authorities =
AuthorityUtils.createAuthorityList("ROLE_USER");
private Map<String, Object> attributes;
private String id;
private String name;
private String login;
private String email;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public Map<String, Object> getAttributes() {
if (this.attributes == null) {
this.attributes = new HashMap<>();
this.attributes.put("id", this.getId());
this.attributes.put("name", this.getName());
this.attributes.put("login", this.getLogin());
this.attributes.put("email", this.getEmail());
}
return attributes;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
id
,name
,login
,和
OAuth 2.0 UserService
DefaultOAuth2UserService
是OAuth2UserService
支持标准OAuth 2.0 Provider的实现。
OAuth2UserService
从UserInfo
端点(通过在授权流程中使用授予客户端的访问令牌)获取最终用户(资源所有者)的用户属性,并返回一个OAuth2User
。
如果默认实现不适合,可以定义自己的OAuth2UserService
。
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.userInfoEndpoint()
.userService(this.oauth2UserService())
...
}
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
return new CustomOAuth2UserService();
}
}
OpenID Connect 1.0 UserService
OidcUserService
是一个OAuth2UserService
支持OpenID Connect 1.0 Provider的实现。
OAuth2UserService
负责(通过使用授权流程中授予给客户机的访问令牌)获得从用户信息端点终端用户的用户属性(资源所有者),并返回一个OidcUser
。
如果默认实现不适合,可以定义自己的OAuth2UserService
OpenID Connect 1.0 Provider的实现。
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(this.oidcUserService())
...
}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
return new CustomOidcUserService();
}
}