使用 Java Config 的 Spring Security 自定义身份验证过滤器

Posted

技术标签:

【中文标题】使用 Java Config 的 Spring Security 自定义身份验证过滤器【英文标题】:Spring Security custom authentication filter using Java Config 【发布时间】:2015-02-14 22:55:04 【问题描述】:

我正在尝试在基本 Web 应用程序中使用 Java 配置配置 Spring Security,以使用 URL 请求参数中提供的加密令牌对外部 Web 服务进行身份验证。

我希望(我认为)有一个安全过滤器来拦截来自登录门户的请求(它们都转到 /authenticate),过滤器将使用 AuthenticationProvider 来处理身份验证过程的业务逻辑。

Login Portal --> Redirect '\authenticate' (+ Token) --> Authenticate Token back to Login Portal (WS) --> 如果成功获取角色和设置用户。

我已经创建了一个过滤器..

@Component
public final class OEWebTokenFilter extends GenericFilterBean 
    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException 
        if (request instanceof HttpServletRequest) 
             OEToken token = extractToken(request);
             // dump token into security context (for authentication-provider to pick up)
             SecurityContextHolder.getContext().setAuthentication(token);
        
       
    chain.doFilter(request, response);

一个身份验证提供者...

@Component
public final class OEWebTokenAuthenticationProvider implements AuthenticationProvider 
    @Autowired
    private WebTokenService webTokenService;

    @Override
    public boolean supports(final Class<?> authentication) 
        return OEWebToken.class.isAssignableFrom(authentication);
    

    @Override
    public Authentication authenticate(final Authentication authentication) 
         if (!(authentication instanceof OEWebToken)) 
             throw new AuthenticationServiceException("expecting a OEWebToken, got " + authentication);
        

        try 
            // validate token locally
            OEWebToken token = (OEWebToken) authentication;
            checkAccessToken(token);

            // validate token remotely
            webTokenService.validateToken(token);

            // obtain user info from the token
            User userFromToken = webTokenService.obtainUserInfo(token);

            // obtain the user from the db
            User userFromDB = userDao.findByUserName(userFromToken.getUsername());

            // validate the user status
            checkUserStatus(userFromDB);

            // update ncss db with values from OE
            updateUserInDb(userFromToken, userFromDB);

            // determine access rights
            List<GrantedAuthority> roles = determineRoles(userFromDB);

            // put account into security context (for controllers to use)
            return new AuthenticatedAccount(userFromDB, roles);
         catch (AuthenticationException e) 
            throw e;
         catch (Exception e) 
             // stop non-AuthenticationExceptions. otherwise full stacktraces returned to the requester
             throw new AuthenticationServiceException("Internal error occurred");
        
    

还有我的 Spring 安全配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    OESettings oeSettings;

    @Bean(name="oeAuthenticationService")
    public AuthenticationService oeAuthenticationService() throws AuthenticationServiceException 
        return new AuthenticationServiceImpl(new OEAuthenticationServiceImpl(), oeSettings.getAuthenticateUrl(), oeSettings.getApplicationKey());
    

    @Autowired
    private OEWebTokenFilter tokenFilter;

    @Autowired
    private OEWebTokenAuthenticationProvider tokenAuthenticationProvider;

    @Autowired
    private OEWebTokenEntryPoint tokenEntryPoint;

    @Bean(name="authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Override
    public void configure(AuthenticationManagerBuilder auth)  throws Exception 
        auth.authenticationProvider(tokenAuthenticationProvider);
    

    @Bean
    public FilterRegistrationBean filterRegistrationBean ()   
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();     
        registrationBean.setFilter(tokenFilter);    
        registrationBean.setEnabled(false);
        return registrationBean;
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/authenticate**").permitAll()
            .antMatchers("/resources/**").hasAuthority("ROLE_USER")
            .antMatchers("/home**").hasAuthority("ROLE_USER")
            .antMatchers("/personSearch**").hasAuthority("ROLE_ADMIN")
            // Spring Boot actuator endpoints
            .antMatchers("/autoconfig**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/beans**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/configprops**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/dump**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/env**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/health**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/info**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/mappings**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/metrics**").hasAuthority("ROLE_ADMIN")
            .antMatchers("/trace**").hasAuthority("ROLE_ADMIN")
            .and()
                .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(tokenAuthenticationProvider)
                .antMatcher("/authenticate/**")
                .exceptionHandling().authenticationEntryPoint(tokenEntryPoint)
            .and()
                .logout().logoutSuccessUrl(oeSettings.getUrl());
    

我的问题是我的 SpringConfig 类中过滤器的配置。我希望过滤器仅在请求 /authenticate URL 时生效,我已将 .antMatcher("/authenticate/**") 添加到过滤器配置中。

.and()
                .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(tokenAuthenticationProvider)
                .antMatcher("/authenticate/**")
                .exceptionHandling().authenticationEntryPoint(tokenEntryPoint)

当我在所有其他 URL 中的该行不再受保护时,我可以手动导航到 /home 而不进行身份验证,删除该行并 /home 进行身份验证。

我应该声明一个仅适用于特定 URL 的过滤器吗?

如何在保持其他 URL 安全的同时实现这一点?

【问题讨论】:

【参考方案1】:

我已通过在调用身份验证提供程序之前检查过滤器中的身份验证状态解决了我的问题....

配置

.and()
    .addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class)
    .authenticationProvider(tokenAuthenticationProvider)
    .exceptionHandling().authenticationEntryPoint(tokenEntryPoint)

过滤器

@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
        throws IOException, ServletException 

    logger.debug(this + "received authentication request from " + request.getRemoteHost() + " to " + request.getLocalName());

    if (request instanceof HttpServletRequest) 
        if (isAuthenticationRequired()) 
            // extract token from header
            OEWebToken token = extractToken(request);

            // dump token into security context (for authentication-provider to pick up)
            SecurityContextHolder.getContext().setAuthentication(token);
         else 
            logger.debug("session already contained valid Authentication - not checking again");
        
    

    chain.doFilter(request, response);


    private boolean isAuthenticationRequired() 
    // apparently filters have to check this themselves.  So make sure they have a proper AuthenticatedAccount in their session.
    Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
    if ((existingAuth == null) || !existingAuth.isAuthenticated()) 
        return true;
    

    if (!(existingAuth instanceof AuthenticatedAccount)) 
        return true;
    

    // current session already authenticated
    return false;

【讨论】:

你能贴出这些类的代码吗:OEWebTokenAuthenticatedAccountextractToken(request) 方法实现?我会为我的休息服务提供很多帮助。提前致谢。 我无法完成这项工作。 this baeldung blog post帮我解决了类似的情况

以上是关于使用 Java Config 的 Spring Security 自定义身份验证过滤器的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Config - Git 存储库错误

spring-batch (java-config) 使用 JobExecutionDecider 识别和执行步骤

Spring Security sec:使用 Java Config 授权标签

无法通过 spring.cloud.config.enabled:false 禁用 Spring Cloud Config

重试不使用带有 Java Config 的 Spring Batch

Spring Security Role Hierarchy 无法使用 Java Config