通过Spring进行RESTfulauthentication

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

要求:

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

  • 客户端随后对各种(保护的)URL进行请求,将以前获得的令牌附加为查询参数(或者不太希望的HTTP请求头)。

  • 客户不能期望存储cookie。

  • 由于我们已经使用了Spring,因此解决scheme应该使用Spring Security。

我们一直在试图做这个工作,我们的头撞墙,所以希望有人已经解决了这个问题。

鉴于上述情况,您如何解决这一特殊需求?

我们设法完全按照OP中的描述进行工作,希望别人能够使用这个解决scheme。 以下是我们所做的:

设置安全上下文,如下所示:

 <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> 

正如你所看到的,我们已经创build了一个自定义的AuthenticationEntryPoint ,如果请求没有通过我们的AuthenticationTokenProcessingFilter在filter链中进行AuthenticationEntryPoint ,基本上只会返回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); } 

这应该让你开始一个好的开始。 快乐的编码。 🙂

您可能会考虑摘要访问身份validation 。 本质上协议如下:

  1. 请求来自客户端
  2. 服务器响应一个唯一的随机数string
  3. 客户端提供用户名和密码(以及其他一些值)md5与随机数散列; 这个哈希被称为HA1
  4. 服务器然后能够validation客户的身份并提供请求的材料
  5. 与nonce的通信可以继续,直到服务器提供新的nonce(计数器用于消除重播攻击)

所有这些通信都是通过标头完成的,正如jmort253指出的那样,通常比在URL参数中传递敏感资料更安全。

摘要访问authentication由Spring Security支持。 请注意,尽pipe文档说您必须有权访问客户端的纯文本密码,但是如果您的客户端具有HA1散列 , 则可以成功进行身份validation 。

关于带有信息的令牌,JSON Web Tokens( http://jwt.io )是一个非常棒的技术。 主要的概念是将信息元素(声明)embedded到令牌中,然后对整个令牌进行签名,以便validation端可以validation声明是否真的可信。

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

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

为什么不开始使用JSON WebToken的OAuth

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

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

你可以在这里find更多的信息