Spring security 吃 angularjs POST 请求

Posted

技术标签:

【中文标题】Spring security 吃 angularjs POST 请求【英文标题】:Spring security eating angularjs POST request 【发布时间】:2016-01-08 17:21:27 【问题描述】:

在使用 spring security 自定义登录表单时,我从 UI 传递的参数在 HttpServletRequest 中无法访问。

class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter 

    private final TokenAuthenticationService tokenAuthenticationService;
    private final CustomJDBCDaoImpl userDetailsService;

    protected StatelessLoginFilter(String urlMapping, TokenAuthenticationService tokenAuthenticationService,
            CustomJDBCDaoImpl userDetailsService, AuthenticationManager authManager) 
        super(new AntPathRequestMatcher(urlMapping));
        this.userDetailsService = userDetailsService;
        this.tokenAuthenticationService = tokenAuthenticationService;
        setAuthenticationManager(authManager);
    

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException 

                final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
                request.getAttribute("email").toString(), request.getAttribute("password").toString());
        return getAuthenticationManager().authenticate(loginToken);
    

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain, Authentication authentication) throws IOException, ServletException 

        final UserDetails authenticatedUser = userDetailsService.loadUserByUsername(authentication.getName());
        final UserAuthentication userAuthentication = new UserAuthentication(authenticatedUser);

        tokenAuthenticationService.addAuthentication(response, userAuthentication);
        SecurityContextHolder.getContext().setAuthentication(userAuthentication);
    

在 AttemptAuthentication 方法中,请求没有采用我使用以下代码从 POST 请求中传递的属性:

 var request = $http.post('/verifyUser', 
       email: 'user', password: 'user',_csrf: $cookies['XSRF-TOKEN'])

我尝试使用调试器控制台对其进行跟踪,发现负载中填充了我转发的元素。

"email":"user","password":"user","_csrf":"f1d88246-28a0-4e64-a988-def4cafa5004"

我的安全配置是:

http
                .exceptionHandling().and()
                .anonymous().and()
                .servletApi().and()
                .headers().cacheControl().and()
                .authorizeRequests()

                //allow anonymous resource requests
                .antMatchers("/").permitAll()               
                //allow anonymous POSTs to login
                .antMatchers(HttpMethod.POST, "/verifyUser").permitAll()
                .and()
                  .formLogin().loginPage("/signin")
                .permitAll()
                .and()

                .addFilterBefore(new StatelessLoginFilter("/verifyUser", new TokenAuthenticationService("456abc"), new CustomJDBCDaoImpl() , authenticationManager()), UsernamePasswordAuthenticationFilter.class)


                .addFilterBefore(new StatelessAuthenticationFilter(new TokenAuthenticationService("456abc")), UsernamePasswordAuthenticationFilter.class).httpBasic()
                         .and().csrf().disable().addFilterBefore(new CSRFFilter(), CsrfFilter.class);

编辑#1

我也尝试使用 getParameter("email") 而不是 getAttribute("email") 但是,此时整个参数映射也是空的。

编辑#2:添加请求内容

Remote Address:127.0.0.1:80
Request URL:http://localhost/api/verifyUser/
Request Method:POST
Status Code:502 Bad Gateway
Response Headers
view source
Connection:keep-alive
Content-Length:583
Content-Type:text/html
Date:Sun, 11 Oct 2015 17:23:24 GMT
Server:nginx/1.6.2 (Ubuntu)
Request Headers
view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:81
Content-Type:application/x-www-form-urlencoded
Cookie:XSRF-TOKEN=f1d88246-28a0-4e64-a988-def4cafa5004
Host:localhost
Origin:http://localhost
Referer:http://localhost/ui/
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
X-XSRF-TOKEN:f1d88246-28a0-4e64-a988-def4cafa5004
Form Data
view source
view URL encoded
"email":"user","password":"user":

【问题讨论】:

【参考方案1】:

您想要的emailpassword 数据是参数,而不是属性。 ServletRequest 中的属性是仅服务器端的数据,您可以在应用程序中使用这些数据在类之间或向 JSP 传递数据。

注意:您必须使用内容类型 application/x-www-form-urlencoded 并确保请求正文以正确的格式编码以在服务器端使用 getParameter,例如email=user&password=user.

默认情况下,Angular 会将对象编码为 JSON

Transforming Requests and Responses

Angular 提供以下默认转换:

请求转换($httpProvider.defaults.transformRequest 和 $http.defaults.transformRequest):

如果请求配置对象的数据属性包含 对象,将其序列化为 JSON 格式。

另见How do I POST urlencoded form data with $http in AngularJS?

Difference between getAttribute() and getParameter()

【讨论】:

嗯,请求的内容类型是什么?如果它不是像application/x-www-form-urlencoded 这样的“表单”类型,那么您需要阅读请求正文并对其进行解析。见docs.oracle.com/javaee/7/api/javax/servlet/… 或docs.oracle.com/javaee/7/api/javax/servlet/… 我已经在 POST 之前明确设置了表单类型。当我检查 request.getHeader("content-type") 时,它返回给我 application/x-www-form-urlencoded。还是我还需要解析请求输入流? 你不应该这样做。我没有使用过AbstractAuthenticationProcessingFilter,但我知道getParameter 在这种情况下适用于过滤器和端点。您可以在服务器看到的情况下发布请求正文的内容吗?也许 Angular 做了一些奇怪的事情,服务器没有以正确的格式获取数据。 啊,我想我看到了问题所在。即使您的请求内容类型是 application/x-www-form-urlencoded,您的请求正文实际上是 JSON 格式(最后一行)。格式应为email=user&password=user,类似于查询字符串。我将编辑我的答案。

以上是关于Spring security 吃 angularjs POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security和Angular 5

Angular集成Spring Boot,Spring Security,JWT和CORS

Angular 2/Spring Security CSRF 实现问题

Angular 4.0 + Spring boot + Spring Security:TemplateInputException:解析模板“登录”时出错

如何使用 Spring Security + Angular 登录/验证

Angular 2 Spring Security CSRF 令牌