通过Spring的RESTful身份验证

问题:

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

要求:

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

  • 客户端向各种(受保护的)URL发出后续请求,将先前获得的令牌附加为查询参数(或者,不太希望是HTTP请求标头)。

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

  • 由于我们已经使用过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。

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

}

以上是 通过Spring的RESTful身份验证 的全部内容, 来源链接: utcz.com/qa/415849.html

回到顶部