通过Spring进行RESTful身份验证 [英] RESTful Authentication via Spring

查看:546
本文介绍了通过Spring进行RESTful身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:

我们有一个基于Spring MVC的RESTful API,其中包含敏感信息。应该保护API,但是不希望向每个请求发送用户凭证(用户/通过组合)。根据REST准则(和内部业务要求),服务器必须保持无状态。另一台服务器将使用mashup风格的方式使用API​​。

Problem:
We have a Spring MVC-based RESTful API which contains sensitive information. The API should be secured, however sending the user's credentials (user/pass combo) with each request is not desirable. Per REST guidelines (and internal business requirements), the server must remain stateless. The API will be consumed by another server in a mashup-style approach.

要求:


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

  • Client makes a request to .../authenticate (unprotected URL) with credentials; server returns a secure token which contains enough information for the server to validate future requests and remain stateless. This would likely consist of the same information as Spring Security's Remember-Me Token.

客户端对各种(受保护)URL进行后续请求,将先前获取的标记附加为查询参数(或者,不太希望的是,HTTP请求标题)。

Client makes subsequent requests to various (protected) URLs, appending the previously obtained token as a query parameter (or, less desirably, an HTTP request header).

不能指望客户存储cookie。

Client cannot be expected to store cookies.

由于我们已经使用Spring,解决方案应该使用Spring Security。

Since we use Spring already, the solution should make use of Spring Security.

我们已经我们一直在试图做这项工作,所以希望那里的人已经解决了这个问题。

We've been banging our heads against the wall trying to make this work, so hopefully someone out there has already solved this problem.

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

Given the above scenario, how might you solve this particular need?

推荐答案

我们设法完全按照OP中的描述使其工作,并希望其他人可以使用该解决方案。以下是我们所做的:

We managed to get this working exactly as described in the OP, and hopefully someone else can make use of the solution. Here's what we did:

设置安全上下文如下:

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

As you can see, we've created a custom AuthenticationEntryPoint, which basically just returns a 401 Unauthorized if the request wasn't authenticated in the filter chain by our AuthenticationTokenProcessingFilter.

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 包含一些私有(并且特定于案例的)代码,无法轻松共享。这是它的界面:

Obviously, TokenUtils contains some privy (and very case-specific) code and can't be readily shared. Here's its interface:

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

这应该让你有个好的开始。快乐的编码。 :)

That ought to get you off to a good start. Happy coding. :)

这篇关于通过Spring进行RESTful身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆