手动验证使用弹簧安全性

时间:2014-08-20 06:43:30

标签: java spring spring-mvc spring-security

我正在使用Spring安全性并且工作正常,但现在我想手动启动安全流程,做客户端更改我需要进入我的控制器的用户名和密码(表单)不会直接调用“j_spring_security_check”)

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

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

    public void test(loginDTO loginDTO){

    MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
    HttpHeaders headers = new HttpHeaders();
    
    body.add(
         "j_username",
         loginDTO.getJ_username());
    
    body.add(
         "j_password",
         loginDTO.getJ_password());
    
    HttpEntity<?> httpEntity = new HttpEntity<Object>(
                              body, headers);
    headers.add(
            "Accept",
            MediaType.APPLICATION_JSON_VALUE);
    restTemplate.exchange(
                  "http://localhost:8080/XXX/j_spring_security_check",
                  HttpMethod.POST,
                  httpEntity,
                  HttpServletResponse.class);
    } 
    
  2. 这不起作用我得到: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。而且感觉不对,我错过了使用弹簧安全的重点。

    2. 正确的原因是什么?

3 个答案:

答案 0 :(得分:2)

我通常会执行以下操作:

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

答案 1 :(得分:0)

如果您希望尽可能使用正常的身份验证流程,则可以创建包含登录信息的模拟HttpServletRequestHttpServletResponseorg.springframework.mock.web.MockHttpServletRequestorg.springframework.mock.web.MockHttpServletResponse)和密码,然后调用

 UsernamePasswordAuthenticationFilter.attemptAuthentication(
            HttpServletRequest request,
            HttpServletResponse response)`

之后您还需要调用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<String, String> params = new HashMap<String, String>();


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

另一种方法是实现自己的身份验证过滤器。这是一个调用AuthenticationManager.authenticate(Authentication authentication)的类。但是这个类还负责调用身份验证的所有内容(AbstractAuthenticationProcessingFilter.doFilter做什么)`

答案 2 :(得分:0)

好的,所以我将@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);
    }


    }

工作正常