programing

스프링 경유 RESTful 인증

copyandpastes 2022. 7. 20. 22:24
반응형

스프링 경유 RESTful 인증

문제:
Spring MVC 기반의 RESTful API에는 기밀 정보가 포함되어 있습니다.API는 보안을 유지해야 하지만 각 요청과 함께 사용자의 자격 증명(사용자/패스 콤보)을 보내는 것은 바람직하지 않습니다.REST 가이드라인(및 내부 비즈니스 요건)에 따라 서버는 스테이트리스 상태를 유지해야 합니다.API는 다른 서버에서 매시업 방식으로 사용됩니다.

요건:

  • 클라이언트는 다음 요구를 합니다..../authenticate(보호되지 않은 URL). 서버는 서버가 향후 요구를 검증하고 스테이트리스 상태를 유지하기에 충분한 정보를 포함하는 안전한 토큰을 반환합니다.이것은 Spring Security의 Remember-Me 토큰과 동일한 정보로 구성됩니다.

  • 클라이언트는 이후 다양한 (보호된) URL에 대해 요구를 하고 이전에 취득한 토큰을 쿼리 파라미터(또는 HTTP 요구 헤더)로 추가합니다.

  • 클라이언트가 쿠키를 저장해야 합니다.

  • 이미 Spring을 사용하고 있기 때문에 솔루션에서는 Spring Security를 활용해야 합니다.

우리는 이 문제를 해결하기 위해 벽에 머리를 부딪쳐 왔다. 그러니 누군가 이미 이 문제를 해결했으면 좋겠다.

위의 시나리오에서 이 특정 요구를 어떻게 해결할 수 있습니까?

OP에 기재된 대로 이 작업을 수행할 수 있었습니다.다른 사람이 이 솔루션을 사용할 수 있으면 좋겠습니다.우리가 한 일은 다음과 같습니다.

다음과 같이 보안 콘텍스트를 설정합니다.

<security:http realm="Protected API" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="CustomAuthenticationEntryPoint">
    <security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
    <security:intercept-url pattern="/authenticate" access="permitAll"/>
    <security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

<bean id="CustomAuthenticationEntryPoint"
    class="com.demo.api.support.spring.CustomAuthenticationEntryPoint" />

<bean id="authenticationTokenProcessingFilter"
    class="com.demo.api.support.spring.AuthenticationTokenProcessingFilter" >
    <constructor-arg ref="authenticationManager" />
</bean>

보다시피, 델은 커스텀을 작성했습니다.AuthenticationEntryPoint기본적으로는 이 명령어는401 Unauthorized필터 체인으로 인증되지 않은 경우AuthenticationTokenProcessingFilter.

Custom Authentication Entry Point:

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
    }
}

인증토큰 처리 필터:

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

    @Autowired UserService userService;
    @Autowired TokenUtils tokenUtils;
    AuthenticationManager authManager;
    
    public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
        this.authManager = authManager;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        @SuppressWarnings("unchecked")
        Map<String, String[]> parms = request.getParameterMap();

        if(parms.containsKey("token")) {
            String token = parms.get("token")[0]; // grab the first "token" parameter
            
            // validate the token
            if (tokenUtils.validate(token)) {
                // determine the user based on the (already validated) token
                UserDetails userDetails = tokenUtils.getUserFromToken(token);
                // build an Authentication object with the user's info
                UsernamePasswordAuthenticationToken authentication = 
                        new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
                // set the authentication into the SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(authentication));         
            }
        }
        // continue thru the filter chain
        chain.doFilter(request, response);
    }
}

에는 몇 가지 비밀 코드가 포함되어 있어 쉽게 공유할 수 없습니다.인터페이스는 다음과 같습니다.

public interface TokenUtils {
    String getToken(UserDetails userDetails);
    String getToken(UserDetails userDetails, Long expiration);
    boolean validate(String token);
    UserDetails getUserFromToken(String token);
}

그렇게 하면 출발이 좋을 거야.

다이제스트 액세스 인증을 고려할 수 있습니다.기본적으로 프로토콜은 다음과 같습니다.

  1. 클라이언트에 대한 요청이 있습니다.
  2. 서버가 고유한 난스 문자열로 응답합니다.
  3. 클라이언트는 난스와 해시된 사용자 이름과 비밀번호(및 기타 값) md5를 제공합니다.이 해시는 HA1이라고 불립니다.
  4. 그러면 서버는 클라이언트의 신원을 확인하고 요청된 자료를 제공할 수 있습니다.
  5. 서버가 새로운 난스를 제공할 때까지 난스와의 통신을 계속할 수 있습니다(카운터는 리플레이 공격을 배제하기 위해 사용됩니다).

이 모든 통신은 헤더를 통해 이루어집니다.jmort253이 지적한 바와 같이 일반적으로는 url 파라미터의 기밀정보를 통신하는 것보다 안전합니다.

다이제스트 액세스 인증은 Spring Security에서 지원됩니다.문서에서는 클라이언트의 일반 텍스트비밀번호에 액세스 할 필요가 있다고 되어 있습니다만, 클라이언트의 HA1 해시가 있으면 인증에 성공할 수 있습니다.

정보가 담긴 토큰에 대해서는 JSON Web Tokens(http://jwt.io)가 훌륭한 기술이다.주요 개념은 정보 요소(클레임)를 토큰에 삽입한 후 토큰 전체에 서명하여 검증 엔드가 클레임이 실제로 신뢰할 수 있는지 확인하는 것입니다.

이 Java 구현을 사용하고 있습니다.https://bitbucket.org/b_c/jose4j/wiki/Home

Spring 모듈(Spring-security-jwt)도 있습니다만, 무엇을 지원하는지 알아보지 못했습니다.

JSON WebTokens에서 OAuth를 사용해 보는 것은 어떨까요?

http://projects.spring.io/spring-security-oauth/

OAuth2는 표준화된 인가 프로토콜/프레임워크입니다.공식 OAuth2 사양에 따라:

자세한 내용은 이쪽에서 확인하실 수 있습니다.

언급URL : https://stackoverflow.com/questions/10826293/restful-authentication-via-spring

반응형