Java 如何限制特定域使用Spring Boot和OAuth2登录

Java 如何限制特定域使用Spring Boot和OAuth2登录,java,spring,oauth,spring-boot,Java,Spring,Oauth,Spring Boot,我已经使用spring boot和Google成功地完成了OAuth2登录,但我想将登录限制在特定域(我们使用Google应用程序进行工作) 我认为应该通过扩展类OAuth2ClientAuthenticationProcessingFilter(如指定的)来处理,但我不确定如何做到这一点 基本上,我希望使用Google OAuth 2.0作为身份提供者,但必须只接受公司用户(@company.com)。根据Stéphane的建议,我想到了这一点,并最终实现了这一点,这对我来说适用于Google

我已经使用spring boot和Google成功地完成了OAuth2登录,但我想将登录限制在特定域(我们使用Google应用程序进行工作)

我认为应该通过扩展类OAuth2ClientAuthenticationProcessingFilter(如指定的)来处理,但我不确定如何做到这一点


基本上,我希望使用Google OAuth 2.0作为身份提供者,但必须只接受公司用户(@company.com)。

根据Stéphane的建议,我想到了这一点,并最终实现了这一点,这对我来说适用于Google+配置文件:

@Configuration
@EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String GOOGLE_PLUS_DOMAIN_ATTRIBUTE = "domain";
    private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
    private static final String CSRF_HEADER_NAME = "X-XSRF-TOKEN";

    @Bean
    public AuthoritiesExtractor authoritiesExtractor(
            @Value("#{'${security.allowed-domains}'.split(',')}") final List<String> allowedDomains) {

        return new AuthoritiesExtractor() {
            @Override
            public List<GrantedAuthority> extractAuthorities(final Map<String, Object> map) {
                if (map != null && map.containsKey(GOOGLE_PLUS_DOMAIN_ATTRIBUTE)) {
                    final String domain = (String) map.get(GOOGLE_PLUS_DOMAIN_ATTRIBUTE);
                    if (!allowedDomains.contains(domain)) {
                        throw new BadCredentialsException("Not an allowed domain");
                    }
                    return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
                }
                return null;
            }
        };
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
        http.antMatcher("/**")
        .authorizeRequests()
        .antMatchers("/logout", "/api/mappings/**", "/public/**").permitAll()
        .anyRequest().hasAuthority("ROLE_USER")
        .and().logout().logoutUrl("/api/logout").logoutSuccessUrl("/logout")
        .and().csrf().csrfTokenRepository(csrfTokenRepository()).ignoringAntMatchers("/api/mappings/**")
        .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        // @formatter:on
    }

    private Filter csrfHeaderFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
                    final FilterChain filterChain) throws ServletException, IOException {

                final CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
                if (csrf != null) {
                    Cookie cookie = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
                    final String token = csrf.getToken();
                    if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                        cookie = new Cookie(CSRF_COOKIE_NAME, token);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }

    private CsrfTokenRepository csrfTokenRepository() {
        final HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName(CSRF_HEADER_NAME);
        return repository;
    }
}
使用Google+配置文件时,地图中提供的资源服务器响应包含域的条目。我只是将此值与配置的允许域进行了比较

希望这有帮助

更新:2019年3月7日,谷歌不推荐Google+API。如果你像我一样,你会收到一封来自谷歌的电子邮件,建议更新你的软件。在我们的例子中,url将被弃用。因此,我在这里发布我的更新配置(使用SpringBoot 1.3.5构建)

请注意,由于属性域已更改其名称,必须在WebSecurity配置适配器中进行微小更改。因此,您需要更换该行:

私有静态最终字符串GOOGLE\u PLUS\u DOMAIN\u ATTRIBUTE=“DOMAIN”


私有静态最终字符串托管\u域\u属性=“hd”

根据Stéphane的建议,我想到了这一点,并最终实现了这一点,这一点对我来说适用于Google+个人资料:

@Configuration
@EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String GOOGLE_PLUS_DOMAIN_ATTRIBUTE = "domain";
    private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
    private static final String CSRF_HEADER_NAME = "X-XSRF-TOKEN";

    @Bean
    public AuthoritiesExtractor authoritiesExtractor(
            @Value("#{'${security.allowed-domains}'.split(',')}") final List<String> allowedDomains) {

        return new AuthoritiesExtractor() {
            @Override
            public List<GrantedAuthority> extractAuthorities(final Map<String, Object> map) {
                if (map != null && map.containsKey(GOOGLE_PLUS_DOMAIN_ATTRIBUTE)) {
                    final String domain = (String) map.get(GOOGLE_PLUS_DOMAIN_ATTRIBUTE);
                    if (!allowedDomains.contains(domain)) {
                        throw new BadCredentialsException("Not an allowed domain");
                    }
                    return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
                }
                return null;
            }
        };
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        // @formatter:off
        http.antMatcher("/**")
        .authorizeRequests()
        .antMatchers("/logout", "/api/mappings/**", "/public/**").permitAll()
        .anyRequest().hasAuthority("ROLE_USER")
        .and().logout().logoutUrl("/api/logout").logoutSuccessUrl("/logout")
        .and().csrf().csrfTokenRepository(csrfTokenRepository()).ignoringAntMatchers("/api/mappings/**")
        .and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        // @formatter:on
    }

    private Filter csrfHeaderFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response,
                    final FilterChain filterChain) throws ServletException, IOException {

                final CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
                if (csrf != null) {
                    Cookie cookie = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
                    final String token = csrf.getToken();
                    if (cookie == null || token != null && !token.equals(cookie.getValue())) {
                        cookie = new Cookie(CSRF_COOKIE_NAME, token);
                        cookie.setPath("/");
                        response.addCookie(cookie);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }

    private CsrfTokenRepository csrfTokenRepository() {
        final HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName(CSRF_HEADER_NAME);
        return repository;
    }
}
使用Google+配置文件时,地图中提供的资源服务器响应包含域的条目。我只是将此值与配置的允许域进行了比较

希望这有帮助

更新:2019年3月7日,谷歌不推荐Google+API。如果你像我一样,你会收到一封来自谷歌的电子邮件,建议更新你的软件。在我们的例子中,url将被弃用。因此,我在这里发布我的更新配置(使用SpringBoot 1.3.5构建)

请注意,由于属性域已更改其名称,必须在WebSecurity配置适配器中进行微小更改。因此,您需要更换该行:

私有静态最终字符串GOOGLE\u PLUS\u DOMAIN\u ATTRIBUTE=“DOMAIN”


私有静态最终字符串托管\u域\u属性=“hd”

我找到了新的解决方案!您应该选择一个:非反应性反应性案例重要的是不要忘记:

非反应性:

@Bean
fun oauth2UserService(rest: WebClient): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return OAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            user
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
fun oauth2UserService(rest: WebClient): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            Mono.just(user)
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
有趣的oauth2UserService(rest:WebClient):oauth2UserService{
val delegate=DefaultOAuth2UserService()
返回OAuth2UserService{request->
val user=delegate.loadUser(请求)
val hd=user.getAttribute(“hd”)
if(hd==“your.domain.name”)
用户
其他的
抛出OAuth2AuthenticationException(OAuth2Error(“无效的_令牌”,“不在团队中”,“不在团队中”)
}
}
反应性:

@Bean
fun oauth2UserService(rest: WebClient): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return OAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            user
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
fun oauth2UserService(rest: WebClient): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            Mono.just(user)
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
有趣的oauth2UserService(rest:WebClient):ReactiveOAuth2UserService{
val delegate=DefaultOAuth2UserService()
返回ReactiveOAuth2UserService{request->
val user=delegate.loadUser(请求)
val hd=user.getAttribute(“hd”)
if(hd==“your.domain.name”)
Mono.just(用户)
其他的
抛出OAuth2AuthenticationException(OAuth2Error(“无效的_令牌”,“不在团队中”,“不在团队中”)
}
}

我找到了新的解决方案!您应该选择一个:非反应性反应性案例重要的是不要忘记:

非反应性:

@Bean
fun oauth2UserService(rest: WebClient): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return OAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            user
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
fun oauth2UserService(rest: WebClient): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            Mono.just(user)
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
有趣的oauth2UserService(rest:WebClient):oauth2UserService{
val delegate=DefaultOAuth2UserService()
返回OAuth2UserService{request->
val user=delegate.loadUser(请求)
val hd=user.getAttribute(“hd”)
if(hd==“your.domain.name”)
用户
其他的
抛出OAuth2AuthenticationException(OAuth2Error(“无效的_令牌”,“不在团队中”,“不在团队中”)
}
}
反应性:

@Bean
fun oauth2UserService(rest: WebClient): OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return OAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            user
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
fun oauth2UserService(rest: WebClient): ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
    val delegate = DefaultOAuth2UserService()
    return ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> { request ->
        val user = delegate.loadUser(request)
        val hd = user.getAttribute<String>("hd")
        if (hd == "your.domain.name")
            Mono.just(user)
        else
            throw OAuth2AuthenticationException(OAuth2Error("invalid_token", "Not in the Team", ""))
    }
}
@Bean
有趣的oauth2UserService(rest:WebClient):ReactiveOAuth2UserService{
val delegate=DefaultOAuth2UserService()
返回ReactiveOAuth2UserService{request->
val user=delegate.loadUser(请求)
val hd=user.getAttribute(“hd”)
if(hd==“your.domain.name”)
Mono.just(用户)
其他的
抛出OAuth2AuthenticationException(OAuth2Error(“无效的_令牌”,“不在团队中”,“不在团队中”)
}
}

您可以做的一件事是定义一个类型为
authorities extractor
的bean,并在那里进行检查(如果电子邮件的类型不匹配,则引发适当的异常)。您可以做的一件事是定义一个类型为
authorities extractor
的bean,并在那里进行检查(如果电子邮件类型不匹配,则引发适当的异常。我的登录过程中未调用此代码。可能我必须配置其他内容?嗨,Fernando。您是否定义了自定义WebSecurity配置适配器并使用EnableOAuth2Sso?对其进行注释。请查看我上面提到的链接中的示例。嗨@FernandoM.Pinheiro,我知道这个线程很旧,但我也有同样的问题。只是想知道你是否记得你是如何解决的?我已经定义了一个Authorities提取器,但没有被提取。谢谢@JuanCarlosGonzález这个代码在我的登录过程中没有被调用。也许我必须配置其他东西?嗨,Fernando。你定义了cus吗tom WebSecurityConfigureAdapter,并使用EnableOAuth2Sso?对其进行了注释。请查看我上面提到的链接中的示例。Hi@FernandoM.Pinheiro,我知道此线程非常旧,但我遇到了相同的问题。只是想知道您是否记得如何解决此问题?我已定义了AuthoritiesExtractor,但它没有被选中ed起来谢谢你JuanCarlosGonzále