java - REST 应用程序的 Spring Security 表单登录

标签 java ajax spring rest spring-security

我在使用 Spring Security 4.0 时遇到了一些小麻烦。

我正在使用 Spring Security 来保护 Web 应用程序和移动应用程序将通过 Ajax 调用消耗的一些 REST 资源。 我的 Web 应用程序开箱即用,但我的 REST 服务不太好。这个想法是使用 http 中的表单登录来登录我的应用程序,但成功登录后,我无法访问我的 protected 资源

要进入代码,这是我的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 version="3.0">
<display-name>ShakePoint CMS</display-name>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/datasource.xml
        /WEB-INF/repositories.xml
        /WEB-INF/facades.xml
        /WEB-INF/spring-mvc-servlet.xml
        /WEB-INF/spring-security.xml
        </param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
        org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<servlet-mapping>
    <servlet-name>spring-mvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

另外,我的spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans  xmlns="http://www.springframework.org/schema/security"
          xmlns:beans="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:security="http://www.springframework.org/schema/security"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">


<beans:bean id="restAuthenticationEntryPoint" class="com.shakepoint.web.controller.rest.auth.RESTAuthenticationEntryPoint"/>
<beans:bean id="restAuthenticationDeniedHandler" class="com.shakepoint.web.controller.rest.auth.RESTAccessDeniedHandler"/>
<beans:bean id="restAuthenticationFailedHandler" class="com.shakepoint.web.controller.rest.auth.RESTAuthenticationFailedHandler"/>
<beans:bean id="restAuthenticationSuccessHandler" class="com.shakepoint.web.controller.rest.auth.RESTAuthenticationSuccessHandler"/>

<beans:bean id="successAuthenticationHandler" class="com.shakepoint.web.auth.SuccessAuthenticationHandler"/>


<security:http create-session="stateless" 
               pattern="/rest/**" 
               use-expressions="true" 
               entry-point-ref="restAuthenticationEntryPoint" 
               authentication-manager-ref="authenticationManager" >

    <!--<security:intercept-url pattern="/account/signin" access="permitAll()"/>-->
    <security:intercept-url pattern="/rest/account/signup" access="permitAll()"/>
    <security:intercept-url pattern="/rest/shop/**" access="hasRole('ROLE_MEMBER')"/>

    <!--<security:http-basic />-->
    <security:form-login login-processing-url="/rest/account/signin"
                         authentication-failure-handler-ref="restAuthenticationFailedHandler"
                         authentication-success-handler-ref="restAuthenticationSuccessHandler"
                         password-parameter="shakepoint_password"
                         username-parameter="shakepoint_username"
                         />
    <!--<security:access-denied-handler ref="restAuthenticationDeniedHandler"/>-->
    <security:csrf disabled="true"/>
</security:http>



<security:http use-expressions="true" >
    <security:intercept-url pattern="/tech/**" access="hasAnyRole('ROLE_TECHNICIAN')"/>
    <security:intercept-url pattern="/admin/**" access="hasAnyRole('ROLE_ADMIN','ROLE_SUPER_ADMIN')"/>
    <security:form-login
        login-page="/signin"
        login-processing-url="/login_to_checkpoint"
        authentication-failure-url="/signin?error"
        authentication-success-handler-ref="successAuthenticationHandler" 
        password-parameter="j_password"
        username-parameter="j_email"/>

    <security:logout logout-success-url="/" logout-url="/j_spring_security_logout"/>
    <security:csrf/>
</security:http>

<security:global-method-security secured-annotations="enabled"/>

在我的 AuthenticationSuccessHandler 和 AuthenticationFailedHandler 上,我仅发送 application/json 内容类型和包含基本授权值(如果成功)的对象,如果失败则发送错误消息。

我已使用以下 ajax 请求成功登录我的应用程序

$.support.corps = true; 
        $.ajax({
            url: 'http://192.168.0.7:8080/rest/account/signin',
            type: "POST",
            data: JSON.stringify(signinData),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (response) {
                if(response.success == true){
                    ...
                }
            },
            error: function(error){
                alert(error);
            }
        }); 

但是当我想访问添加基本授权 header 的 protected 资源时,它会从服务器返回未经授权的状态。 我还注意到 spring 正在响应上创建一个 JSESSION cookie。

我的 UserDetailsS​​erivce 工作正常,但是,真正的问题是:

Am I missing any header? or any extra setup from my server side project?

请提供任何帮助,我们将不胜感激

[更新] 这是使用 Spring Security 成功登录的 Tomcat-7 日志

2015-10-07 12:16:18 DEBUG AntPathRequestMatcher:151 - Checking match of         request : '/rest/account/signin'; against '/rest/**'
2015-10-07 12:16:18 DEBUG FilterChainProxy:324 - /rest/account/signin?  shakepoint_password=tech2&shakepoint_username=tech2@gmail.com at position 1 of 9    in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-10-07 12:16:18 DEBUG FilterChainProxy:324 - /rest/account/signin?shakepoint_password=tech2&shakepoint_username=tech2@gmail.com at position 2 of 9 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2015-10-07 12:16:18 DEBUG FilterChainProxy:324 - /rest/account/signin?shakepoint_password=tech2&shakepoint_username=tech2@gmail.com at position 3 of 9 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2015-10-07 12:16:18 DEBUG HstsHeaderWriter:128 - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7cb9682a
2015-10-07 12:16:18 DEBUG FilterChainProxy:324 - /rest/account/signin?shakepoint_password=tech2&shakepoint_username=tech2@gmail.com at position 4 of 9 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2015-10-07 12:16:18 DEBUG AntPathRequestMatcher:151 - Checking match of request : '/rest/account/signin'; against '/rest/account/signin'
2015-10-07 12:16:18 DEBUG UsernamePasswordAuthenticationFilter:211 - Request is to process authentication
2015-10-07 12:16:18 DEBUG ProviderManager:162 - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2015-10-07 12:16:18 DEBUG JdbcTemplate:634 - Executing prepared SQL query
 2015-10-07 12:16:18 DEBUG JdbcTemplate:569 - Executing prepared SQL statement [select email, password, role from user where email = ?]
2015-10-07 12:16:18 DEBUG DataSourceUtils:110 - Fetching JDBC Connection from DataSource    
2015-10-07 12:16:18 DEBUG DriverManagerDataSource:162 - Creating new JDBC  DriverManager Connection to [jdbc:mysql://localhost:3306/shakepoint]
2015-10-07 12:16:19 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
2015-10-07 12:16:19 DEBUG UsernamePasswordAuthenticationFilter:317 - Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@511e4d66: Principal: org.springframework.security.core.userdetails.User@fea4fb4d: Username: tech2@gmail.com; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_TECHNICIAN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_TECHNICIAN
2015-10-07 12:16:19 DEBUG SecurityContextPersistenceFilter:105 - SecurityContextHolder now cleared, as request processing completed

但是,在我尝试访问我的 protected 资源后,我得到了这个:

2015-10-07 12:22:09 DEBUG AntPathRequestMatcher:151 - Checking match of request : '/rest/shop/secured_ping'; against '/rest/**'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 2 of 9 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 3 of 9 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2015-10-07 12:22:09 DEBUG HstsHeaderWriter:128 - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@7cb9682a
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 4 of 9 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2015-10-07 12:22:09 DEBUG AntPathRequestMatcher:131 - Request 'GET /rest/shop/secured_ping' doesn't match 'POST /rest/account/signin
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 5 of 9 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 6 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at   position 7 of 9 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2015-10-07 12:22:09 DEBUG AnonymousAuthenticationFilter:100 - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2015-10-07 12:22:09 DEBUG FilterChainProxy:324 - /rest/shop/secured_ping at position 9 of 9 in additional filter chain; firing Filter:       'FilterSecurityInterceptor'
2015-10-07 12:22:09 DEBUG AntPathRequestMatcher:151 - Checking match of request : '/rest/shop/secured_ping'; against '/rest/account/signup'
2015-10-07 12:22:09 DEBUG AntPathRequestMatcher:151 - Checking match of request : '/rest/shop/secured_ping'; against '/rest/shop/**'
2015-10-07 12:22:09 DEBUG FilterSecurityInterceptor:218 - Secure object: FilterInvocation: URL: /rest/shop/secured_ping; Attributes: [hasRole('ROLE_MEMBER')]
2015-10-07 12:22:09 DEBUG FilterSecurityInterceptor:347 - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2015-10-07 12:22:09 DEBUG AffirmativeBased:65 - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@629d991f, returned: -1
2015-10-07 12:22:09 DEBUG ExceptionTranslationFilter:173 - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at        org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter.doFilter(DefaultLoginPageGeneratingFilter.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2522)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2511)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
2015-10-07 12:22:09 DEBUG ExceptionTranslationFilter:202 - Calling Authentication entry point.
2015-10-07 12:22:09 DEBUG SecurityContextPersistenceFilter:105 - SecurityContextHolder now cleared, as request processing completed

最佳答案

好吧,这次我找到了自己的答案,在深入了解 Spring 文档后,我发现 Spring Security 需要一个 BasicAuthorizationProcessingFilter在应用程序上设置 SecurityContext

我唯一做的就是:

添加身份验证过滤器

<beans:bean id="basicAuthenticationFilter"   class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
    <beans:constructor-arg index="0" ref="authenticationManager"/> 
    <beans:constructor-arg index="1" ref="restAuthenticationEntryPoint"/> 
</beans:bean>

更改了我的 http 元素,添加了自定义过滤器子元素

<security:http create-session="stateless" 
               pattern="/rest/**" 
               use-expressions="true" 
               entry-point-ref="restAuthenticationEntryPoint" 
               authentication-manager-ref="authenticationManager" >

    <!--<security:intercept-url pattern="/account/signin" access="permitAll()"/>-->
    <security:intercept-url pattern="/rest/account/signup" access="permitAll()"/>
    <security:intercept-url pattern="/rest/shop/**" access="hasAnyRole('ROLE_MEMBER', 'ROLE_SUPER_ADMIN', 'ROLE_ADMIN', 'ROLE_TECHNICIAN')"/>
    <security:custom-filter ref="basicAuthenticationFilter" position="BASIC_AUTH_FILTER"/>
    <security:form-login login-processing-url="/rest/account/signin"
                         authentication-failure-handler-ref="restAuthenticationFailedHandler"
                         authentication-success-handler-ref="restAuthenticationSuccessHandler"
                         password-parameter="shakepoint_password"
                         username-parameter="shakepoint_username"
                         />
    <!--<security:access-denied-handler ref="restAuthenticationDeniedHandler"/>-->
    <security:csrf disabled="true"/>
</security:http>

进行这些更改后,我的应用程序安全性开箱即用!

关于java - REST 应用程序的 Spring Security 表单登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32984810/

相关文章:

java - 有哪些适用于 Android 的 websocket 客户端?

java - 在用户键入时编辑 shell 前缀

javascript - 如何使用ajax将更新推送到表单中

java - Spring MVC 重定向是在 url 中添加一些参数

spring - 如何通过 spring boot 和 ejb 集成修复 hibernate validator 问题

java - 在最后一个节点之前添加第一个节点

java - 扩展可比接口(interface)并重写 compareTo

javascript - Ajax 无法正确获取数据

javascript - 为ajax加载图像

java - Dozer Mapper 未映射组成的嵌套字段