通过扩展WebSecurityConfigurerAdapter,如何用自定义认证逻辑来构建配置

问题描述 投票:1回答:1

我正在使用okta来进行身份验证,我们公司的okta禁用了 "默认 "授权服务器。我们公司的okta禁用了 "默认 "授权服务器。所以现在我不能使用 "okta-spring-security-starter "来简单地验证url头传递的token。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class OktaOAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/health").permitAll()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer().jwt();

        http.cors();

        Okta.configureResourceServer401ResponseBody(http);

    }
}

所以我需要使用okta内测端点(https:/developer.okta.comdocsreferenceapioidc#introspect。)来验证。所以我想知道,我是否可以将这个过程整合到配置的 WebSecurityConfigurerAdapter......也许是这样的?

import com.okta.spring.boot.oauth.Okta;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class OktaOAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/health").permitAll()
                .anyRequest().authenticated()
                .and()
                /*add something there*/

        http.cors();


    }
}

我看到了一些类似于override AuthenticationProvider(具有Spring安全和Java配置的自定义身份验证提供商。),并使用httpbasic auth。如果我使用.oauth2ResourceServer().jwt(),能不能做类似的事情。

我的想法是重写认证提供者,然后在提供者中打okta introspect端点,这样可以吗?

java spring spring-boot okta okta-api
1个回答
2
投票

Spring Security 5.2支持自省端点。请看一下 不透明令牌样本 在GitHub repo中。

不过在这里简单回答一下,你可以这样做。

http
    .authorizeRequests(authz -> authz
        .anyRequest().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .opaqueToken(opaque -> opaque
            .introspectionUri("the-endpoint")
            .introspectionClientCredentials("client-id", "client-password")
        )
    );

如果你使用的是Spring Boot,那就简单一些。你可以在你的 application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        opaquetoken:
          introspection-uri: ...
          client-id: ...
          client-secret: ...

然后你的DSL就可以指定 opaqueToken:

http
    .authorizeRequests(authz -> authz
        .anyRequest().authenticated()
    )
    .oauth2ResourceServer(oauth2 -> oauth2
        .opaqueToken(opaque -> {})
    );

2
投票

我没有使用Okta,因此我不知道它到底是如何工作的。但我有两个假设。

  • 每个请求都在授权头中包含一个访问令牌。
  • 你向${baseUrl}v1introspect发出POST请求,它将用true或false回答你,以表明accessToken是否有效。

在这2个假设的前提下,如果我必须手动实现自定义安全逻辑认证,我会做以下步骤。

  • 注册并实现一个 CustomAuthenticationProvider
  • 添加一个过滤器,从请求中提取访问令牌。

注册自定义认证提供者。

// In OktaOAuth2WebSecurityConfig.java
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customAuthenticationProvider());
}

@Bean
CustomAuthenticationProvider customAuthenticationProvider(){
    return new CustomAuthenticationProvider();
}

CustomAuthenticationProvider:

public class CustomAuthenticationProvider implements AuthenticationProvider {

private static final Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    logger.debug("Authenticating authenticationToken");
    OktaTokenAuthenticationToken auth = (OktaTokenAuthenticationToken) authentication;
    String accessToken = auth.getToken();

    // You should make a POST request to ${oktaBaseUrl}/v1/introspect
    // to determine if the access token is good or bad

    // I just put a dummy if here

    if ("ThanhLoyal".equals(accessToken)){
        List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("USER"));
        logger.debug("Good access token");
        return new UsernamePasswordAuthenticationToken(auth.getPrincipal(), "[ProtectedPassword]", authorities);
    }
    logger.debug("Bad access token");
    return null;
}

@Override
public boolean supports(Class<?> clazz) {
    return clazz == OktaTokenAuthenticationToken.class;
}

}

注册过滤器,从请求中提取accessToken。

// Still in OktaOAuth2WebSecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .addFilterAfter(accessTokenExtractorFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests().anyRequest().authenticated();
            // And other configurations

}

@Bean
AccessTokenExtractorFilter accessTokenExtractorFilter(){
    return new AccessTokenExtractorFilter();
}

以及过滤器本身:

public class AccessTokenExtractorFilter extends OncePerRequestFilter {

private static final Logger logger = LoggerFactory.getLogger(AccessTokenExtractorFilter.class);

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    logger.debug("Filtering request");
    Authentication authentication = getAuthentication(request);
    if (authentication == null){
        logger.debug("Continuing filtering process without an authentication");
        filterChain.doFilter(request, response);
    } else {
        logger.debug("Now set authentication on the request");
        SecurityContextHolder.getContext().setAuthentication(authentication);
        filterChain.doFilter(request, response);
    }
}

private Authentication getAuthentication(HttpServletRequest request) {
    String accessToken = request.getHeader("Authorization");
    if (accessToken != null){
        logger.debug("An access token found in request header");
        List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("USER"));
        return new OktaTokenAuthenticationToken(accessToken, authorities);
    }

    logger.debug("No access token found in request header");
    return null;
}

}

我在这里上传了一个简单的项目,方便大家参考。https:/github.comMrLoyalspring-security-custom-authentication。

它是如何工作的。

  • AccessTokenExtractorFilter被放置在UsernamePasswordAuthenticationFilter之后,这是Spring Security默认的过滤器。
  • 一个请求到达后,上面的过滤器会从中提取accessToken,并把它放在SecurityContext中。
  • 之后,AuthenticationManager调用AuthenticationProvider来验证请求。在这种情况下,调用了CustomAuthenticationProvider。

另外,你的问题应该包含 spring-security 标签。

更新1: 关于认证入口点

AuthenticationEntryPoint 声明当一个未经认证的请求到达时该怎么做(在我们的例子中,当请求不包含一个有效的 "授权 "头时该怎么做)。

在我的REST API中,我只是简单地响应401 HTTP状态码给客户端。

// CustomAuthenticationEntryPoint
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    response.reset();
    response.setStatus(401);
    // A utility method to add CORS headers to the response
    SecUtil.writeCorsHeaders(request, response);
}

Spring的 LoginUrlAuthenticationEntryPoint 重定向用户到登录页面(如果配置了登录页面)。

因此,如果您想将未经认证的请求重定向到Okta的登录页面,您可以使用AuthenticationEntryPoint。

© www.soinside.com 2019 - 2024. All rights reserved.