Announcement Announcement Module
Collapse
No announcement yet.
Two <http> container elements with one for the browser and one for REST Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Two <http> container elements with one for the browser and one for REST

    Hello,

    I have a first <http> element to define a form based browser authentication:
    Code:
    	<http auto-config="true" use-expressions="true">
    		<intercept-url pattern="/admin/login" access="permitAll" />
    		<intercept-url pattern="/admin/logout" access="permitAll" />
    		<intercept-url pattern="/admin/denied" access="permitAll" />
    		<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
    		<form-login
    			login-page="/admin/login"
    			default-target-url="/admin/list"
    			authentication-failure-url="/admin/denied?failed=true"
    			always-use-default-target="true" />
    		<logout logout-success-url="/admin/login" />
    		<logout delete-cookies="JSESSIONID" />
    		<session-management invalid-session-url="/invalidSession" />
    	</http>
    It uses a custom authentication provider:
    Code:
    	<authentication-manager>
            <authentication-provider ref="customAuthenticationProvider" />
    	</authentication-manager>
    It works just fine.

    Now, I'm adding some REST request controller and I would like to have a slightly different behavior of its authentication, that is, a successful authentication should not do a redirect but simply return a 200 OK response and a failed authentication should not do a redirect either but simply return a 401 UNAUTHORIZED response.

    I could implement this as well with a second <http> element:
    Code:
    	<http use-expressions="true" pattern="/admin/json/**">
    		<intercept-url pattern="/admin/json/**" access="hasRole('ROLE_ADMIN')" />
    		<http-basic entry-point-ref="restAuthenticationEntryPoint" />
    		<form-login authentication-success-handler-ref="restSuccessHandler" />
    		<logout />
    	</http>
    	<beans:bean id="restSuccessHandler"
    		class="com.thalasoft.learnintouch.web.security.RestUrlAuthenticationSuccessHandler" />
    This second <http> element works fine when the first one is commented out.

    But there is still something that is not right when both <http> elements are enabled.

    In that case, if I do the following call:
    curl -i "http://localhost:8080/learnintouch-web/admin/json/list" --user stephane:wrongpassword
    that is, with some wrong user credentials then the RestAuthenticationEntryPoint is called correctly and I get the expected HTTP/1.1 401 Unauthorized response.

    But if I do the following call:
    curl -i "http://localhost:8080/learnintouch-web/admin/json/list"
    that is, without any user credentials then the RestAuthenticationEntryPoint is not called and I get an unexpected HTTP/1.1 302 Found response.

    Any idea why this is happening ?

    Thanks.

  • #2
    Another issue I have is that when calling the http://localhost:8080/learnintouch-web/admin/json/list url from within the browser, instead of receiving the expected 401 UNAUTHORIZED response I receive a redirection to the login page.

    That tells me the first <http> element is being called on here, instead of the second one.

    Comment


    • #3
      Each <http> element is considered in order so make sure that you list the /admin/json/** first. Otherwise the form based login matches everything (this means that the /admin/json/** one will never be reached).

      Comment


      • #4
        HI Rob,

        Thanks for your input. Yes, my "/admin/json/**" is already listed first in the xml file, sitting above the other one.

        I thought about that too at first.

        But I also had to resort to use the pattern="/admin/json" attribute in the first <http> element.

        My understanding is that, with this attribute, the listing order should not matter. But I may be wrong on that one.

        Comment


        • #5
          Here is the console output:
          2013-07-09 13:07:46,189 DEBUG [AntPathRequestMatcher] Checking match of request : '/admin/json/list'; against '/admin/json/**'
          2013-07-09 13:07:46,189 DEBUG [FilterChainProxy] /admin/json/list at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
          2013-07-09 13:07:46,190 DEBUG [HttpSessionSecurityContextRepository] No HttpSession currently exists
          2013-07-09 13:07:46,190 DEBUG [HttpSessionSecurityContextRepository] No SecurityContext was available from the HttpSession: null. A new one will be created.
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 2 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 3 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 4 of 11 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
          2013-07-09 13:07:46,190 DEBUG [AnonymousAuthenticationFilter] Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.Anony mousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.We bAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
          2013-07-09 13:07:46,190 DEBUG [FilterChainProxy] /admin/json/list at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
          2013-07-09 13:07:46,190 DEBUG [AntPathRequestMatcher] Checking match of request : '/admin/json/list'; against '/admin/json/**'
          2013-07-09 13:07:46,190 DEBUG [FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /admin/json/list; Attributes: [hasRole('ROLE_ADMIN')]
          2013-07-09 13:07:46,190 DEBUG [FilterSecurityInterceptor] Previously Authenticated: org.springframework.security.authentication.Anonym ousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.We bAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
          2013-07-09 13:07:46,190 DEBUG [AffirmativeBased] Voter: org.springframework.security.web.access.expression .WebExpressionVoter@1d1d7e6, returned: -1
          2013-07-09 13:07:46,191 DEBUG [ExceptionTranslationFilter] Access is denied (user is anonymous); redirecting to authentication entry point
          org.springframework.security.access.AccessDeniedEx ception: Access is denied
          Last edited by stephaneeybert; Jul 9th, 2013, 06:11 AM.

          Comment


          • #6
            The chapter "8.6 Advanced Namespace Configuration" at http://static.springsource.org/sprin...chains-with-ns
            lead me to try to have the following pattern="/admin/json/**" in the <intercept-url> of the first <http> element, as in:
            Code:
            	<http use-expressions="true" pattern="/admin/json/**" create-session="stateless">
            		<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
            		<http-basic entry-point-ref="restAuthenticationEntryPoint" />
            		<form-login authentication-success-handler-ref="restSuccessHandler" />
            		<logout />
            	</http>
            Doing a curl request with a wrong password behaves correctly:

            curl -i "http://localhost:8080/learnintouch-web/admin/json/list" --user stephane:wrongpassword
            HTTP/1.1 401 Unauthorized
            Server: Apache-Coyote/1.1
            WWW-Authenticate: Basic realm="LearnInTouch REST"
            Content-Length: 106
            Date: Tue, 09 Jul 2013 16:09:34 GMT
            HTTP Status 401 - The login and password could not match for stephane
            You failed the REST authentication.

            But the http://localhost:8080/learnintouch-web/admin/json/list request in the web browser responds with a redirect to the http://localhost:8080/learnintouch-w...security_login url.

            I wonder why.
            Last edited by stephaneeybert; Jul 9th, 2013, 11:23 AM.

            Comment


            • #7
              Do you really intend to use form-login for your admin URLs? This is going to redirect you to a login page. The http-basic@entry-point-ref is only used for a failed basic authentication, not prompting for credentials when none are present. I'd remove form-login and add http@entry-point-ref="restAuthenticationEntryPoint".

              Comment


              • #8
                I have a controller with methods handling requests from jsp pages and for these the form login works now perfectly.

                The form login is not supposed to kick in on a REST request.

                So, yes, I intend to use the form login for some admin urls, when these are from jsp pages. And the redirection should occur if needed.

                I'm trying to have the REST requests being secured with the first <http> element.

                Comment


                • #9
                  I think you are going to need to use DelegatingAuthenticationEntryPoint and assign it to http@entry-point-ref. This will allow Spring Security to use one entry point for your REST requests and another for your HTML requests. You will need to create a RequestMatcher instance that matches your REST requests (i.e. perhaps it looks at the content type). If it matches, you will delegate to the restAuthenticationEntryPoint. If it does not match, then you will delegate to a LoginUrlAuthenticationEntryPoint. Of course you can switch the logic and have a RequestMatcher that finds HTML requests and delegate to LoginUrlAuthenticationEntryPoint, else delegate to restAuthenticationEntryPoint. See the javadoc of DelegatingAuthenticationEntryPoint for an example of configuring it.

                  Comment


                  • #10
                    Hi Rob,

                    First, thanks for your input. It shapes up my understanding of it all a bit.

                    If I get your suggestion, and implement it, I will have to move all this logic out of XML and into some Java class.

                    Code:
                      <http auto-config="true" use-expressions="true">
                        <intercept-url pattern="/admin/login" access="permitAll" />
                        <intercept-url pattern="/admin/logout" access="permitAll" />
                        <intercept-url pattern="/admin/denied" access="permitAll" />
                        <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
                        <form-login
                          login-page="/admin/login"
                          default-target-url="/admin/list"
                          authentication-failure-url="/admin/denied?failed=true"
                          always-use-default-target="true" />
                        <logout logout-success-url="/admin/login" />
                        <logout delete-cookies="JSESSIONID" />
                      </http>
                    I'm surprised I cannot stick to XML for such a simple user requirement as having two security realms, one for HTML pages requests and one for REST requests.

                    As a reminder, each of these two security realms work perfectly fine, when the other one is commented out.

                    Comment


                    • #11
                      Another question aside of the issue...

                      Do you know if the two patterns bellow are cumulative ?
                      Code:
                      <http auto-config="true" use-expressions="true" pattern="/admin/**">
                        <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
                      In that case, I should have instead:
                      Code:
                      <http auto-config="true" use-expressions="true" pattern="/admin/**">
                        <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
                      Or have:
                      Code:
                      <http auto-config="true" use-expressions="true" pattern="/**">
                        <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />

                      Comment


                      • #12
                        No you should not need to abandon the XML configuration. I'm a big lost as to what your configuration looks like now, but taking the previous post as an example you would do something like this:

                        Code:
                        <http use-expressions="true" pattern="/admin/json/**" entry-point-ref="entryPoint">
                            <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
                            <http-basic entry-point-ref="restAuthenticationEntryPoint" />
                            <!-- if you want this http element to authenticate (and thus use its success handler) you need to have login-processing-url match the pattern on http (i.e. start with /admin/json) -->
                            <form-login authentication-success-handler-ref="restSuccessHandler" login-processing-url="/admin/json/authenticate"/>
                            <logout />
                        </http>
                        
                        <b:bean id="entryPoint" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
                             <b:constructor-arg>
                                 <b:map>
                                     <!-- this is creating a ELRequestMatcher , you could also do a custom RequestMatcher implementation which would be more powerful -->
                                     <b:entry key="hasHeader('Accept','application/json')" value-ref="restAuthenticationEntryPoint" />
                                     <!-- alternative to the above entry which is more powerful You need to create a bean named restRequestMatcher that implements RequestMatcher and returns true when it is a rest request -->
                                     <b:entry key-ref="restRequestMatcher" value-ref="restAuthenticationEntryPoint" />
                                 </b:map>
                             </b:constructor-arg>
                             <b:property name="defaultEntryPoint" ref="formLoginEntryPoint"/>
                         </b:bean>
                        
                        <b:bean id="formLoginEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
                            <!-- keep in mind the form must submit the the http element that you want to process it. This means you will want it to submit to a URL that starts with /admin/json/authenticate for this example -->
                            <b:property name="loginFormUrl" value="/login.jsp"/>
                        </b:bean>

                        Comment


                        • #13
                          After this I now have a: j_spring_security_check that finds no matching filters when submitting the login form:
                          2013-07-10 16:07:51,243 DEBUG [AntPathRequestMatcher] Checking match of request : '/j_spring_security_check'; against '/admin/json/**'
                          2013-07-10 16:07:51,243 DEBUG [AntPathRequestMatcher] Checking match of request : '/j_spring_security_check'; against '/admin/**'
                          2013-07-10 16:07:51,244 DEBUG [FilterChainProxy] /j_spring_security_check has no matching filters
                          I guess that was due to my trying adding a pattern="/admin/**" in the second <http> element. So I remove it.
                          Last edited by stephaneeybert; Jul 10th, 2013, 09:13 AM.

                          Comment


                          • #14
                            Yes see my previous response comments in the config. In short:

                            * Thinks of each <http> as a "slice" of your application that is considered in order. The slice is split up using http@pattern or http@request-matcher-ref.
                            * If a requset matches the <http> block no other <http> blocks are considered
                            * Each URL or pattern within the <http> block is independent of the http@pattern (merging these would be too complex especially when considering the request-matcher-ref can be anything). So for example if http@pattern="/admin/json/**" and intercept-url@pattern="/something", the intercept-url will never be matched. Likewise, if if http@pattern="/admin/json/**" and your form-login@login-processing-url="/j_spring_security_check" then it will never match and try to authenticate the user (form-login is never considered unless it matches http@pattern)

                            For more details see this webinar recording I gave http://www.youtube.com/watch?v=k32KqrckLEE

                            Comment


                            • #15
                              Hi Rob,

                              I'm not sure what you are suggesting answers what I try to achieve.

                              Let my take some height again and sum things up.

                              I have a working JSP based authentication with a security real:
                              Code:
                              	<http auto-config="true" use-expressions="true">
                              		<intercept-url pattern="/admin/login" access="permitAll" />
                              		<intercept-url pattern="/admin/logout" access="permitAll" />
                              		<intercept-url pattern="/admin/denied" access="permitAll" />
                              		<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
                              		<form-login
                              			login-page="/admin/login"
                              			default-target-url="/admin/list"
                              			authentication-failure-url="/admin/denied?failed=true"
                              			always-use-default-target="true" />
                              		<logout logout-success-url="/admin/login" />
                              		<logout delete-cookies="JSESSIONID" />
                              	</http>
                              Now, I would like to also secure the lately added REST controller.

                              For this, I would like to have some security, but one that behaves a bit differently than the one used by the JSP pages.

                              This REST based authentication should either return a 200 and no redirection, or should return a 401 and no redirection.

                              Comment

                              Working...
                              X