문제:
우리는 Spring MVC 기반의 RESTful API를 가지고 있으며, 여기에는 중요한 정보가 포함되어 있습니다. API는 보안되어야 하지만 각 요청과 함께 사용자의 자격 증명(사용자/패스 콤보)을 보내는 것은 바람직하지 않습니다. REST 지침(및 내부 비즈니스 요구 사항)에 따라 서버는 상태 비저장 상태를 유지해야 합니다. API는 매시업 스타일의 접근 방식으로 다른 서버에 의해 소비됩니다.
요구 사항:
클라이언트는 자격 증명을 사용하여 '.../authenticate'(보호되지 않은 URL)에 요청을 하고, 서버는 서버가 향후 요청을 검증하고 상태 비저장 상태를 유지할 수 있는 충분한 정보를 포함하는 보안 토큰을 반환합니다. 이는 Spring Security'의 [Remember-Me Token][1]과 동일한 정보로 구성될 수 있습니다.
클라이언트는 이전에 얻은 토큰을 쿼리 매개 변수(또는 덜 바람직하게는 HTTP 요청 헤더)로 추가하여 다양한 (보호된) URL에 후속 요청을 합니다.
클라이언트에서 쿠키를 저장할 수 없습니다.
우리는 이미 Spring을 사용하고 있기 때문에 솔루션은 Spring Security를 사용해야 합니다.
우리는 이 문제를 해결하려고 머리를 벽에 부딪혀 왔기 때문에 누군가 이미 이 문제를 해결했기를 바란다.
위의 시나리오를 고려할 때, 이 특정한 요구를 어떻게 해결할 수 있습니까?
[1]http://static.springsource.org/spring-security/site/docs/3.1.x/reference/remember-me.html
우리는 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>
보시는 바와 같이 사용자 지정 인증 엔트리 포인트를 만들었습니다. 인증에 의해 필터 체인에서 인증되지 않은 경우 기본적으로 "401 Unauthorized"만 반환됩니다.토큰 처리 필터'입니다.
사용자 정의인증 진입점:
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);
}
}
분명히 *'TokenUtils'는 일부 개인 정보(그리고 매우 사례별) 코드를 포함하고 있어 쉽게 공유할 수 없습니다. 인터페이스는 다음과 같습니다.
public interface TokenUtils {
String getToken(UserDetails userDetails);
String getToken(UserDetails userDetails, Long expiration);
boolean validate(String token);
UserDetails getUserFromToken(String token);
}
그럼 출발이 좋겠군요. 행복한 코딩. :)
[Digest Access Authentication][1]을(를) 고려할 수 있습니다. 기본적으로 프로토콜은 다음과 같습니다.
이 모든 통신은 jmort253이 지적한 바와 같이 일반적으로 URL 매개 변수의 민감한 자료를 통신하는 것보다 더 안전한 헤더를 통해 이루어진다.
다이제스트 액세스 인증은 [Spring Security][2]에서 지원됩니다. 문서에 클라이언트의 일반 텍스트 암호에 대한 액세스 권한이 있어야 한다고 나와 있지만 클라이언트에 대한 HA1 해시[3]가 있으면 [인증 성공]할 수 있습니다.
[1]http://tools.ietf.org/html/rfc2617#section-3 [2]http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#digest-processing-filter [3]https://jira.springsource.org/browse/SEC-1905
정보를 전달하는 토큰에 대해서는 JSON 웹토큰(http://jwt.io)이 뛰어난 기술이다. 주요 개념은 정보 요소(청구)를 토큰에 내장한 다음 전체 토큰에 서명하여 검증 엔드가 클레임이 실제로 신뢰할 수 있는지 확인할 수 있도록 하는 것이다.
저는 자바 구현을 사용합니다: https://bitbucket.org/b_c/jose4j/wiki/Home
스프링 모듈(spring-security-jwt)도 있지만 어떤 모듈을 지원하는지 알아본 적이 없다.
http://projects.spring.io/spring-security-oauth/
OAuth2 백업이었습니다 표준화했습니다 인증 프로토콜 / 레임워크. 이에 따라 OAuth2 사양명세:
Here 더욱 다양한 정보를 얻으실 수 있습니다