带有 spring 安全性的 OAuth 2 并在重定向中设置 State 参数 [英] OAuth 2 with spring security and setting the State parameter in the redirect

查看:38
本文介绍了带有 spring 安全性的 OAuth 2 并在重定向中设置 State 参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用带有 Spring 安全性的 Spring Boot,自定义过滤器"类调用带有 OAuth 2 身份验证的 CIAM 服务器.我想显式设置或覆盖默认设置,以便我可以在 Spring Security 在后台准备的重定向 URL 中设置自定义动态 STATE 参数,并将用户发送到 CIAM 服务器登录页面.这对我来说是微不足道的,但事实证明远非如此.

I am using Spring boot with Spring security, with custom "Filter" Class calling to CIAM server with OAuth 2 authentication. I want to set explicitly or override the default setting so that I could set custom dynamic STATE parameter in the redirect URL that Spring Security prepares under the hood and sends the user to the CIAM server login page. This seamed trivial to me but it turned out to be far from that.

目标是添加 OAuth2 重定向链接的自定义 STATE 参数,以便在身份验证完成且 CIAM 服务器将我重定向回我的页面后,我取回自动包含在成功重定向链接中的 STATE 参数来自CIAM 服务器.

The goal is to add the custom STATE parameter of the OAuth2 redirect link so that after the authentication is finished and the CIAM server redirects me back to my page I take back the STATE parameter which is automatically included in the successful redirect link from the CIAM server.

安全配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, 
proxyTargetClass = true)
@EnableOAuth2Client
@Order(3)
public class OAuth2LoginWebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
CiamOAuth2ClientFilter oAuth2CiamClientFilter;

@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
    return new InMemoryUserDetailsManager();
}



@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/**/*.css", "/**/*.png", "/**/*.gif", "/**/*.jpg", "/h2-console/**", "/css/**",
            "/img/**", "/font-awesome/**", "/fonts/**", "/js/**", "/signout","/signout/**", "/health");
}

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {

    httpSecurity
        .antMatcher("/**")
            .authorizeRequests()
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/backoffice/**").hasRole("ADMIN")
                .antMatchers("/api/**").hasRole("API")
                .antMatchers(/*"/", */"/login**", "/webjars/**", "/favicon.*", "/resources/**", 
                        "/auth/**", "/signin/**","css/**","js/**", "/signup/**", "/signout/", "/health", "/awsTest/login")
                    .permitAll()
                .anyRequest()
                    .authenticated()
                    .and()
                        .exceptionHandling()
                            .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login/callback"))
                            .and()
                                .addFilterBefore(oAuth2CiamClientFilter.ciamFilter(), BasicAuthenticationFilter.class)
        .logout()
            .logoutUrl("/signout")
            .logoutSuccessUrl("/logout");
}

}

自定义过滤器类

@Configuration
public class CiamOAuth2ClientFilter {

@Autowired
AuthorizationCodeResourceDetails oauth2CiamResourceDetails;

@Autowired
CiamOAuth2ClientProperties oauth2CiamClientProperties;

@Autowired
OAuth2ClientContext oauth2ClientContext;

@Autowired
CiamPrincipalExtractor ciamPrincipalExtractor;

@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(filter);
    registration.setOrder(-100);
    registration.addInitParameter("test", "trrrrrrr");

    System.out.println("333333333333333333333333");
    System.out.println(registration);
    return registration;
}

public Filter ciamFilter() {

    System.out.println("postaeget");
    System.out.println(oauth2CiamClientProperties);
    System.out.println(" _-------------------------------: " + oauth2CiamClientProperties.getResource().getUserInfoUri());

    UserInfoTokenServices tokenService = new UserInfoTokenServices(oauth2CiamClientProperties.getResource().getUserInfoUri(), oauth2CiamResourceDetails.getClientId());
    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oauth2CiamResourceDetails, oauth2ClientContext);
    OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter("/login/callback");

    tokenService.setRestTemplate(restTemplate);
    tokenService.setPrincipalExtractor(ciamPrincipalExtractor);
    filter.setRestTemplate(restTemplate);
    filter.setTokenServices(tokenService);



    return filter;
}

}

与问题相关的应用程序 yml 设置文件

Application yml settings file connected with the issue

security:
oauth2:
client:

  clientId: ...
  clientSecret: ....
  accessTokenUri: ...
  userAuthorizationUri: ...
  useCurrentUri: false
  preEstablishedRedirectUri: https://localhost/login/callback
  clientAuthenticationScheme: query
  authenticationScheme: header
  serverLogoutUrl: ..
  postLogoutRedirectUri: https://localhost/signout
  scope:
    - openid
    - profile
    - email
    - offline_access
  state: TEST
resource:
  userInfoUri: ...
  preferTokenInfo: ...

推荐答案

In my case

我在@Configuration 的某处配置了 OAuth2ClientAuthenticationProcessingFilter:

I configure OAuth2ClientAuthenticationProcessingFilter somewhere in @Configuration:

private Filter ssoFilter() {
    OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(API_LOGIN_FACEBOOK);
    OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
    AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProviderWithUrl = new AuthorizationCodeAccessTokenProvider();
    authorizationCodeAccessTokenProviderWithUrl.setStateKeyGenerator(new StateKeyGeneratorWithRedirectUrl());
    facebookTemplate.setAccessTokenProvider(authorizationCodeAccessTokenProviderWithUrl);
    facebookFilter.setRestTemplate(facebookTemplate);
    UserInfoTokenServices tokenServices = new CheckedUserInfoTokenServices(
            facebookResource().getUserInfoUri(), facebook().getClientId(),
            facebookPrincipalExtractor, blogPreAuthenticationChecks(), blogPostAuthenticationChecks());
    tokenServices.setAuthoritiesExtractor(new FacebookAuthoritiesExtractor());
    tokenServices.setRestTemplate(facebookTemplate);
    facebookFilter.setTokenServices(tokenServices);
    facebookFilter.setAuthenticationSuccessHandler(new OAuth2AuthenticationSuccessHandler());
    return facebookFilter;
}

并且您可以使用

RequestContextHolder.getRequestAttributes()

因此您可以提取 Referer 标头,例如:

so you can extract Referer header for example:

public class StateKeyGeneratorWithRedirectUrl extends DefaultStateKeyGenerator {
    private RandomValueStringGenerator generator = new RandomValueStringGenerator();

    @Override
    public String generateKey(OAuth2ProtectedResourceDetails resource) {
        HttpServletRequest currentHttpRequest = getCurrentHttpRequest();
        if (currentHttpRequest!=null){
            String referer = currentHttpRequest.getHeader("Referer");
            if (!StringUtils.isEmpty(referer)){
                return generator.generate()+","+referer;
            }
        }
        return generator.generate();
    }

    private static HttpServletRequest getCurrentHttpRequest(){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes instanceof ServletRequestAttributes) {
            return ((ServletRequestAttributes)requestAttributes).getRequest();
        }
        return null;
    }
}

下一步 - 从回调读取状态:

Next - read state from callback:

public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    public static final String DEFAULT = "/";

    @Override
    protected String determineTargetUrl(HttpServletRequest request,
                                        HttpServletResponse response) {

        UriComponents uriComponents = UriComponentsBuilder.newInstance()
            .query(request.getQueryString())
            .build();

        MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
        String stateEncoded = queryParams.getFirst("state");
        if (stateEncoded == null) {
            return DEFAULT;
        }
        String stateDecoded = URLDecoder.decode(stateEncoded, StandardCharsets.UTF_8);
        String[] split = stateDecoded.split(",");
        String redirect;
        if (split.length != 2){
            return DEFAULT;
        } else {
            return split[1];
        }
    }
}

这篇关于带有 spring 安全性的 OAuth 2 并在重定向中设置 State 参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆