programing

Java Config를 사용한 Spring Security 사용자 지정 인증 필터

goodsources 2023. 11. 2. 21:40
반응형

Java Config를 사용한 Spring Security 사용자 지정 인증 필터

기본 웹 어플리케이션에서 Java config를 사용하여 URL 요청 파라미터에 제공된 암호화된 토큰을 사용하여 외부 웹 서비스에 대한 인증을 수행하도록 Spring Security를 구성하려고 합니다.

Login Portal(로그인 포털)의 요청을 가로채는 보안 필터(모두 인증/인증)가 있으면 좋겠습니다. 이 필터는 Authentication Provider(인증 공급자)를 사용하여 인증 프로세스의 비즈니스 로직을 처리합니다.

로그인 포털 --> '\authenticate'(+토큰) --> 토큰을 다시 로그인 포털(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");
        }
    }

그리고 나의 스프링 보안 구성

@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 클래스의 필터 구성입니다.필터 구성에 .antMatcher("/authenticate/**")를 추가하여 /authenticate URL에 대한 요청일 때만 필터가 적용되도록 합니다.

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

다른 모든 URL에 이 줄이 있으면 인증하지 않고 수동으로 /home으로 이동하여 줄을 제거하면 /home이 인증됩니다.

특정 URL에만 적용 가능한 필터를 선언해야 합니까?

다른 URL의 보안을 유지하면서 이를 구현하려면 어떻게 해야 합니까?

인증 공급자를 참여시키기 전에 필터에서 인증 상태를 확인하여 문제를 해결했습니다. ..

구성

.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;
}

언급URL : https://stackoverflow.com/questions/27507862/spring-security-custom-authentication-filter-using-java-config

반응형