programing

Spring Security - 토큰 기반 API 인증 및 사용자/비밀번호 인증

magicmemo 2023. 9. 8. 21:22
반응형

Spring Security - 토큰 기반 API 인증 및 사용자/비밀번호 인증

저는 스프링을 이용하여 REST API를 주로 제공할 웹앱을 만들고 보안 측면을 구성하려고 합니다.

저는 이런 종류의 패턴을 구현하려고 노력하고 있습니다: http://web.archive.org/web/20130822184827/https (Google은 그 페이지를 완전히 바꿨기 때문에 더 이상 말이 되지 않습니다 - 제가 여기서 언급했던 페이지를 보세요: developers.google.com/accounts/docs/MobileApps) ://https://developers.google.com/accounts/docs/MobileApps

제가 해야 할 일은 다음과 같습니다.

  • 웹 앱은 일반적인 봄 사용자/비밀번호 인증과 함께 작동하는 간단한 로그인/가입 양식을 가지고 있습니다. (이전에 dao/인증관리자/사용자상세서비스 등에서 이러한 종류의 작업을 수행한 적이 있습니다.)
  • 상태 비저장 세션 및 요청과 함께 제공된 토큰을 기반으로 인증된 모든 요청인 REST api 끝점

(예: 일반 양식을 이용한 사용자 로그인/가입, webapp에서 다음 API 요청 시 사용할 수 있는 토큰으로 안전한 쿠키 제공)

다음과 같이 일반 인증 설정을 받았습니다.

@Override protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("/resources/**").permitAll()
            .antMatchers("/mobile/app/sign-up").permitAll()
            .antMatchers("/v1/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/")
            .loginProcessingUrl("/loginprocess")
            .failureUrl("/?loginFailure=true")
            .permitAll();
}

요청에 있는 토큰을 확인한 다음 보안 컨텍스트를 설정하는 사전 인증 필터를 추가하려고 생각했습니다(즉, 일반적인 다음 인증이 생략된다는 뜻입니까?). 하지만 토큰 기반 보안에 대해 너무 많은 작업을 수행하지 않은 일반 사용자/비밀번호를 넘어서,하지만 몇 가지 다른 예를 바탕으로 다음과 같은 것을 생각해 냈습니다.

보안 구성:

@Override protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .addFilter(restAuthenticationFilter())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and()
                .antMatcher("/v1/**")
            .authorizeRequests()
                .antMatchers("/resources/**").permitAll()
                .antMatchers("/mobile/app/sign-up").permitAll()
                .antMatchers("/v1/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/loginprocess")
                .failureUrl("/?loginFailure=true")
                .permitAll();
    }

내 사용자 지정 휴식 필터:

public class RestAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public RestAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    private final String HEADER_SECURITY_TOKEN = "X-Token"; 
    private String token = "";


    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        this.token = request.getHeader(HEADER_SECURITY_TOKEN);

        //If we have already applied this filter - not sure how that would happen? - then just continue chain
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }

        //Now mark request as completing this filter
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);

        //Attempt to authenticate
        Authentication authResult;
        authResult = attemptAuthentication(request, response);
        if (authResult == null) {
            unsuccessfulAuthentication(request, response, new LockedException("Forbidden"));
        } else {
            successfulAuthentication(request, response, chain, authResult);
        }
    }

    /**
     * Attempt to authenticate request - basically just pass over to another method to authenticate request headers 
     */
    @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        AbstractAuthenticationToken userAuthenticationToken = authUserByToken();
        if(userAuthenticationToken == null) throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
        return userAuthenticationToken;
    }


    /**
     * authenticate the user based on token, mobile app secret & user agent
     * @return
     */
    private AbstractAuthenticationToken authUserByToken() {
        AbstractAuthenticationToken authToken = null;
        try {
            // TODO - just return null - always fail auth just to test spring setup ok
            return null;
        } catch (Exception e) {
            logger.error("Authenticate user by token error: ", e);
        }
        return authToken;
    }

위 내용은 실제로 앱을 시작할 때 다음과 같은 오류가 발생합니다.authenticationManager must be specified이 작업을 수행하는 가장 좋은 방법을 알려줄 수 있는 사람이 있습니까? 사전 인증 필터가 이 작업을 수행하는 가장 좋은 방법입니까?


편집

표준 토큰 구현(OAuth가 아닌)을 구현하는 Spring-security(코드 포함)를 사용하여 찾은 내용과 수행 방법을 작성했습니다.

문제의 개요 및 접근/해결 방법

Spring-security로 솔루션 구현

다른 사람들에게 도움이 되길 바랍니다.

저는 당신이 언급한 오류가 단지 그 이유가AbstractAuthenticationProcessingFilter당신이 사용하고 있는 기본 클래스는 필요합니다.AuthenticationManager. 사용하지 않을 경우 노옵(No-op)으로 설정하거나,Filter직접적으로.당신의Filter할 수 을고할수다를 합니다.SecurityContext그러면 일반적으로 다운스트림 처리가 생략됩니다(다운스트림 필터 구현에 따라 다르지만, 앱에서 이상한 점이 보이지 않으므로 모두 그렇게 동작할 수 있습니다).

체인 API 를 의 할 을 이라면 가 할 을 가 이라면 WebSecurityConfigurerAdapter콩). 하지만 그것은 사물을 읽기 쉽게 만들 뿐이지, 꼭 중요한 것은 아닙니다.

(댓글에 제시된 것처럼) 바퀴를 다시 개발하게 되었지만 시도하는 과정에서 아무런 해가 되지 않는다는 것을 알게 될 수 있으며, 그 과정에서 스프링과 보안에 대해 더 많이 알게 될 것입니다.

추가: Github 접근법은 꽤 흥미롭습니다. 사용자는 기본 인증에서 토큰을 암호로 사용하고 서버는 사용자 지정 필터가 필요 없습니다.BasicAuthenticationFilter괜찮습니다).

언급URL : https://stackoverflow.com/questions/21994348/spring-security-token-based-api-auth-user-password-authentication

반응형