Java知识储备程序猿阵线联盟-汇总各类技术干货Java学习笔记

Spring Boot整合Spring Security简记-O

2018-02-08  本文已影响198人  78240024406c

new無语 转载请注明原创出处,谢谢!

Spring Security学习目录

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资源):

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 的默认实现是HttpSessionOAuth2AuthorizationRequestRepositoryOAuth2AuthorizationRequestHttpSession中存储。

如果想提供一个自定义实现AuthorizationRequestRepository 存储OAuth2AuthorizationRequestCookie。例子:

@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 的缺省实现是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

@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
它允许您访问的OAuth2UserRequestOAuth2User(使用OAuth 2.0用户UserService时)或OidcUserRequestOidcUser(使用 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;
        };
    }
}

如果默认实现(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;
    }
}

idnamelogin,和email是属性在GitHub的的UserInfo响应返回。有关从UserInfo端点返回的详细信息,请参阅“获取已认证的用户”的API文档。

OAuth 2.0 UserService
DefaultOAuth2UserServiceOAuth2UserService 支持标准OAuth 2.0 Provider的实现。

OAuth2UserServiceUserInfo端点(通过在授权流程中使用授予客户端的访问令牌)获取最终用户(资源所有者)的用户属性,并返回一个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();
}

}

上一篇下一篇

猜你喜欢

热点阅读