【问题标题】:Manually authenticate use spring security手动验证使用spring security
【发布时间】:2014-10-13 10:22:23
【问题描述】:

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

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

  1. 在我得到参数并做一些事情后,我会向 j_spring_security_check url 发送一个 post 请求。我的代码:

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

这不起作用,我得到: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 没有被调用。感觉不对,我错过了使用 Spring Security 的意义。

正确的原因是什么?

【问题讨论】:

  • 在大多数情况下,您认为您需要按照您的要求去做,但实际上您是从错误的角度看待问题(因此会遇到困难)。您能否更准确地说明您到底要解决什么问题,我可能会提出不同的方法?
  • 问题是登录表单不在我的控制之下,它需要工作的方式是我的控制器使用用户名和密码获取登录 dto,我需要使用 spring security 对其进行身份验证.

标签: java spring spring-mvc spring-security


【解决方案1】:

当您想尽可能多地使用正常的身份验证过程时,您可以创建一个包含登录名和密码的模拟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 所做的)`

【讨论】:

  • 我正在尝试做 filter.doFilter。方法需要 FilteChain 怎么获取?
【解决方案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 等进行身份验证,而无需担心自定义代码。

【讨论】:

  • 这与我的第二个选项非常相似。但随后不会调用 onAuthenticationSuccess。
【解决方案3】:

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


    }

效果很好

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-14
    • 2019-07-18
    • 2013-12-28
    • 2014-10-22
    • 2011-11-14
    • 2014-07-29
    • 2012-09-04
    • 1970-01-01
    相关资源
    最近更新 更多