手动validation使用弹簧安全性

我正在使用spring security并且工作正常,但是现在我想手动启动安全过程,对客户端进行更改我需要在控制器中获取用户名和密码(表单不会直接调用“j_spring_security_check”)

我想到了两个选项,我有一些问题:

  1. 在我获取参数并执行某些操作后,我将向j_spring_security_check url发送一个发布请求。 我的代码:

    public void test(loginDTO loginDTO){

    MultiValueMap body = new LinkedMultiValueMap(); HttpHeaders headers = new HttpHeaders(); body.add( "j_username", loginDTO.getJ_username()); body.add( "j_password", loginDTO.getJ_password()); HttpEntity httpEntity = new HttpEntity( body, headers); headers.add( "Accept", MediaType.APPLICATION_JSON_VALUE); restTemplate.exchange( "http://localhost:8080/XXX/j_spring_security_check", HttpMethod.POST, httpEntity, HttpServletResponse.class); } 

这不起作用,我得到:500内部服务器错误为什么?

  1. 第二种选择 – 我做了以下事情:

     public void test2(loginDTO loginDTO, HttpServletRequest request) { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( loginDTO.getJ_username(), loginDTO.getJ_password()); token.setDetails(new WebAuthenticationDetails(request)); Authentication authentication = this.authenticate(token); SecurityContextHolder.getContext().setAuthentication(authentication); this.sessionRegistry.registerNewSession( request.getSession().getId(), authentication.getPrincipal()); } 

    问题是没有调用onAuthenticationSuccess。 感觉不对,我错过了使用弹簧安全的重点。

什么是正确的原因?

我通常会做以下事情:

 @Controller public class AuthenticationController { @Autowired AuthenticationManager authenticationManager; @Autowired SecurityContextRepository securityContextRepository; @RequestMapping(method = Array(RequestMethod.POST), value = Array("/authenticate")) public String authenticate(@RequestParam String username, @RequestParam String password, HttpServletRequest request, HttpServletResponse response) { Authentication result = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); SecurityContextHolder.getContext.setAuthentication(result); this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); return "successView"; } } 

使用这种方法的原因是:

  1. 非常简单,只需几行代码就可以忽略exception处理等等。
  2. 利用现有的Spring Security组件。
  3. 使用在应用程序配置中配置的Spring Security组件,并允许在需要时更改它们。 例如,可以针对RDBMS,LDAP,Web服务,Active Directory等进行身份validation,而无需担心自定义代码。

当您想要从正常的身份validation过程尽可能多地使用时,您可以创建一个模拟的HttpServletRequestHttpServletResponseorg.springframework.mock.web.MockHttpServletRequestorg.springframework.mock.web.MockHttpServletResponse ),其中包含登录名和密码,然后调用

  UsernamePasswordAuthenticationFilter.attemptAuthentication( HttpServletRequest request, HttpServletResponse response)` 

之后你还需要调用SessionAuthenticationStrategy.onAuthentication(..)SessionAuthenticationStrategy.onAuthentication(..) successfulAuthentication(..)

这有点棘手,因为私有文件,所以这是我的解决方案:

 public class ExtendedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Override public void manualAuthentication(String login, String password, HttpServletRequest httpServletRequest) throws IOException, ServletException { /** I do not mock the request, I use the existing request and manipulate them*/ AddableHttpRequest addableHttpRequest = new AddableHttpRequest(httpServletRequest); addableHttpRequest.addParameter("j_username", login); addableHttpRequest.addParameter("j_password", password); MockHttpServletResponse mockServletResponse = new MockHttpServletResponse(); Authentication authentication = this.attemptAuthentication( addableHttpRequest, mockServletResponse); this.reflectSessionStrategy().onAuthentication( authentication, addableHttpRequest, mockServletResponse); this.successfulAuthentication(addableHttpRequest, mockServletResponse, authentication); } private SessionAuthenticationStrategy reflectSessionStrategy() { Field sessionStrategyField = ReflectionUtils.findField( AbstractAuthenticationProcessingFilter.class, "sessionStrategy", SessionAuthenticationStrategy.class); ReflectionUtils.makeAccessible(sessionStrategyField); return (SessionAuthenticationStrategy) ReflectionUtils.getField(sessionStrategyField, this); } } 

AddableHttpRequest就像一个基于真实请求的模拟

 public class AddableHttpRequest extends HttpServletRequestWrapper { /** The params. */ private HashMap params = new HashMap(); public AddableHttpRequest(HttpServletRequest request) { super(request); } @Override public String getMethod() { return "POST"; } @Override public String getParameter(final String name) { // if we added one, return that one if (params.get(name) != null) { return params.get(name); } // otherwise return what's in the original request return super.getParameter(name); } public void addParameter(String name, String value) { params.put(name, value); } } 

另一种方法是实现自己的身份validationfilter。 这是一个调用AuthenticationManager.authenticate(Authentication authentication) 。 但是这个类还负责调用身份validation的所有东西( AbstractAuthenticationProcessingFilter.doFilter做什么)

好的,所以我结合了@Ralph和@manish的答案,这就是我做的:

(twoFactorAuthenticationFilter是UsernamePasswordAuthenticationFilter的扩展)

  public void manualAuthentication(loginDTO loginDTO, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { AddableHttpRequest addableHttpRequest = new AddableHttpRequest( request); addableHttpRequest.addParameter( "j_username", loginDTO.getJ_username()); addableHttpRequest.addParameter( "j_password", loginDTO.getJ_password()); UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) twoFactorAuthenticationFilter.attemptAuthentication( addableHttpRequest, response); if (token.isAuthenticated()) { twoFactorAuthenticationFilter.successfulAuthentication( addableHttpRequest, response, null, token); } } 

它工作正常