基于Spring的RESTful认证

< p > 问题: < br > 我们有一个基于Spring mvc的RESTful API,其中包含敏感信息。API应该是安全的,但是在每个请求时发送用户的凭证(user/pass组合)是不可取的。根据REST准则(和内部业务需求),服务器必须保持无状态。该API将被另一个服务器以混搭方式使用

要求:

  • 客户端使用凭证向.../authenticate(未受保护的URL)发出请求;服务器返回一个安全令牌,其中包含足够的信息,以便服务器验证未来的请求并保持无状态。这可能包含与Spring Security的Remember-Me Token相同的信息。

  • 客户端向各种(受保护的)url发出后续请求,将先前获得的令牌作为查询参数(或者,不太理想的HTTP请求头)追加。

  • 不能期望客户端存储cookie。

  • 因为我们已经使用了Spring,所以解决方案应该使用Spring Security。

我们一直在碰壁,试图让这个工作,所以希望有人已经解决了这个问题。

对于上面的场景,您如何解决这个特殊的需求呢?

127583 次浏览

你可以考虑摘要接入认证。基本上协议如下:

  1. 客户端发出请求
  2. 服务器响应一个唯一的nonce字符串
  3. 客户端提供了一个用户名和密码(和一些其他值)md5哈希与nonce;这个哈希值称为HA1
  4. 然后,服务器能够验证客户机的身份并提供所请求的材料
  5. 与nonce的通信可以继续,直到服务器提供一个新的nonce(计数器用于消除重放攻击)

所有这些通信都是通过头进行的,正如jmort253指出的那样,这通常比在url参数中通信敏感内容更安全。

摘要访问身份验证由Spring Security支持。注意,尽管文档说你必须访问客户端的明文密码,但你可以为客户端如果您有HA1散列,则成功验证

我们设法让它完全按照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,如果请求没有通过我们的AuthenticationTokenProcessingFilter在过滤器链中进行身份验证,它基本上只返回一个401 Unauthorized

CustomAuthenticationEntryPoint:

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

AuthenticationTokenProcessingFilter:

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

这应该会让你有一个良好的开端。

对于承载信息的令牌,JSON Web tokens (http://jwt.io)是一项出色的技术。主要概念是将信息元素(声明)嵌入到令牌中,然后对整个令牌进行签名,以便验证端可以验证声明确实是值得信任的。

我使用这个Java实现:https://bitbucket.org/b_c/jose4j/wiki/Home

还有一个Spring模块(Spring -security-jwt),但我还没有研究它支持什么。

为什么你不开始使用OAuth JSON WebTokens

< a href = " http://projects.spring。Io /spring-security-oauth/" rel="nofollow noreferrer">http://projects.spring.io/spring-security-oauth/

OAuth2是一个标准化的授权协议/框架。根据官方OAuth2 规范:

你可以找到更多信息在这里