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

  • #16
    In that case, the DelegatingAuthenticationEntryPoint configuration I posted should be applied to this configuration. Essentially what is going to happen is you can say in some instances redirect to a login form and in other instances use a custom entry point that you wrote (i.e. your restAuthenticationEntryPoint).

    Comment


    • #17
      To sum things up, my initial configuration works fine even with the two realms enabled, except for one little thing.

      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.

      Why the absence of user credentials is not seen as wrong user credentials ?

      That is the issue. Nothing more.

      Thanks for the webinar, I shall watch it now.

      Comment


      • #18
        Yes DelegatingAuthenticationEntryPoint will help with that issue. Think of it this way...when a URL is requested that requires authentication and no user is authentication Spring Security needs to do something. AuthenticationEntryPoint is what controls that.

        For <form-login> the AuthenticationEntryPoint that is used is the LoginUrlAuthenticationEntryPoint so that is automatically set when using <form-login>.

        For <http-basic> the AuthenticationEntryPoint that is used is the BasicAuthenticationEntryPoint. You can also configure a DIFFERENT AuthenticationEntryPoint which is used when authentication fails with BasicAuthenticationFilter using http-basic@entry-point-ref. This is independent of what is going to happen when an anonymous user attempts to access a URL that requires authentication. I believe it is a bit of a passivity thing...in more modern Spring Security this AuthenticationEntryPoint would probably be better as an AuthenticationFailureHandler which didn't exist until 3.0

        So the question is if you want to do two different things when an anonymous user requests a URL that requires authentication is how does Spring Security know when to use a redirect and when to use your custom entry point? Spring Security doesn't know the difference between URLs you want to protect with REST and URLs you want to protect with a browser. This is where DelegatingAuthenticationEntryPoint comes in. It uses a RequestMatcher interface to say if the current request matches a given RequestMatcher I will use the corresponding AuthenticationEntryPoint. So for your circumstance you want to map some requests to the LoginUrlAuthenticationEntryPoint and other requests to your custom restAuthenticationEntryPoint.

        The configuration I posted above is an example of how to configure the AuthenticationEntryPoint to be a DelegatingAuthenticationEntryPoint that is used when an anonymous user tries to access a protected URL that requires authentication. Please use that as a template for how to do your configuration. You will likely need to update the logic for what you consider to be a REST request (I don't know what that logic is, but you can create a custom RequestMatcher implementation that works for your application).

        HTH

        Comment


        • #19
          Hello Rob,

          Thanks for the time you spend on my issue. I see you are the lead developer of Spring Security and am thankful to and impressed by your attention.

          I wish I were standing next to you to explain the issue :-)

          I may be under a false assumption but you seem to think I want to do different things on one same URL:
          So the question is if you want to do two different things when an anonymous user requests a URL that requires authentication is how does Spring Security know when to use a redirect and when to use your custom entry point?
          I wonder why you say:
          So the question is if you want to do two different things when an anonymous user requests a URL that requires authentication
          Because I don't want to do two different things on one same URL.
          Note that all requests handled by a REST controller have the /admin/json/* pattern and so these requests are caught with the pattern attribute of the first <http> element.

          On the difficulty of written communication :-)

          Kind Regards,

          Comment


          • #20
            I think I must be misunderstanding what you are trying to achieve. You stated:

            Originally posted by stephaneeybert View Post
            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.
            So is the first <http> element always represent a REST request. Specifically will a URL that starts with /admin/json/ ever use form login? If URLs starting with /admin/json will never use form login, then you should remove the form-login from the http@pattern="/admin/json/**" block as I suggested previously. Specifically you would do something like:
            Code:
            <http use-expressions="true" pattern="/admin/json/**">
                <intercept-url pattern="/admin/json/**" access="hasRole('ROLE_ADMIN')" />
                <http-basic entry-point-ref="restAuthenticationEntryPoint" />
                <logout/>
            </http>
            <beans:bean id="restSuccessHandler"
                class="com.thalasoft.learnintouch.web.security.RestUrlAuthenticationSuccessHandler" />
            <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" delete-cookies="JSESSIONID" />
                <session-management invalid-session-url="/invalidSession" />
            </http>
            If that is not the case, please describe what you consider to be a RESTful URL.

            Comment


            • #21
              Hi Rob,

              Now we understand each other :-)

              Yes, URLs starting with a "/admin/json" pattern will never use any form login.

              So I will remove the <form-login> element from the first <http> element, the one with the @pattern="/admin/json/**" pattern.

              Why did I put this <form-login> element in there then ?

              It was to avoid the default redirection on successful authentication, for it does not make sense for a REST request. I wanted to override the authentication success handler to prevent the redirection on a successful authentication. And instead of returning a: 301 MOVED PERMANENTLY simply return a: 200 OK

              Here it is:
              Code:
              public class RestUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
              
                  private RequestCache requestCache = new HttpSessionRequestCache();
              
                  @Override
                  public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws ServletException, IOException {
                      final SavedRequest savedRequest = requestCache.getRequest(request, response);
              
                      if (savedRequest == null) {
                          super.onAuthenticationSuccess(request, response, authentication);
                          return;
                      }
              
                      final String targetUrlParameter = getTargetUrlParameter();
                      
                      if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
                          requestCache.removeRequest(request, response);
                          super.onAuthenticationSuccess(request, response, authentication);
                          return;
                      }
              
                      clearAuthenticationAttributes(request);
                  }
              
                  public void setRequestCache(final RequestCache requestCache) {
                      this.requestCache = requestCache;
                  }
              
              }
              Now comes the question: if I remove this <form-login> element then how can I avoid the redirection on a successful authentication ?

              Comment


              • #22
                It should just work if you are hitting the BasicAuthenticationFilter because it does not use a success handler (it continues processing the original URL)

                Comment


                • #23
                  My bow to you Sir !

                  I simply removed the <form-login authentication-success-handler-ref="restSuccessHandler" /> markup from the <http> element.

                  And it suddenly all behaved perfectly, either from a command line curl request or from the browser.

                  I always get the REST failed authentication response when sending a URL request with the "/admin/json/**" pattern.

                  stephane@stephane-ThinkPad-X60:learnintouch> 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: Wed, 10 Jul 2013 19:32:53 GMT

                  HTTP Status 401 - The login and password could not match for stephane
                  You failed the REST authentication.
                  stephane@stephane-ThinkPad-X60:learnintouch> curl -i "http://localhost:8080/learnintouch-web/admin/json/list"
                  HTTP/1.1 401 Unauthorized
                  Server: Apache-Coyote/1.1
                  Set-Cookie: JSESSIONID=2986C3FC19C14504CB106FE4B20F1CD5; Path=/learnintouch-web/; HttpOnly
                  WWW-Authenticate: Basic realm="LearnInTouch REST"
                  Content-Length: 110
                  Date: Wed, 10 Jul 2013 19:32:55 GMT

                  HTTP Status 401 - Full authentication is required to access this resource
                  You failed the REST authentication.
                  stephane@stephane-ThinkPad-X60:learnintouch>

                  Thank you Rob !

                  Comment


                  • #24
                    Hi Rob,

                    I found about this usage of the <form-login> element in the blog article in which it reads:

                    By default, form login will answer a successful authentication request with a 301 MOVED PERMANENTLY status code; this makes sense in the context of an actual login form which needs to redirect after login. For a RESTful web service however, the desired response for a successful authentication should be 200 OK.
                    Maybe you would have a valuable comment to share there for us.

                    Kind Regards,

                    Comment


                    • #25
                      The same scenario, just post final config

                      Hi, stephane and rob, thanks a lot for this long and concise explanation of a common issue. I have to implement just the same use case in an application with successful jsp-form-login authentication.

                      Stephane, could you please just post your final xml config along with any extra java-stub code you needed to achieve the thing working? It would be very useful for me.

                      Thanks a lot in advance,
                      David

                      Comment


                      • #26
                        Here it is:

                        Code:
                        	<!-- A REST authentication -->
                        	<http use-expressions="true" pattern="/admin/json/**">
                        		<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
                        		<http-basic entry-point-ref="restAuthenticationEntryPoint" />
                        		<logout />
                        	</http>
                        
                        	<!-- A form based browser authentication -->
                        	<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>
                        Code:
                        @Component
                        public final class RestAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
                        
                            @Override
                            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
                                response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\"");
                                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                                PrintWriter writer = response.getWriter();
                                writer.println("HTTP Status 401 - " + authenticationException.getMessage());
                                writer.println("You failed the REST authentication.");
                            }
                        
                            @Override
                            public void afterPropertiesSet() throws Exception {
                                setRealmName("MyApp REST");
                                super.afterPropertiesSet();
                            }
                            
                        }

                        Comment


                        • #27
                          Thanks a lot Stephane, it is very clear.

                          I have one follow-up question... why do you always set "401 unauthorized" in the "commence" method of the entry point? does it mean that whenever the pattern matches that entrypoint the authentication fails?

                          It is the only thing I don't have perfectly clear here, I suppose you can check the credentials or something in that method of the EntryPoint.

                          Thanks in advance,

                          David

                          Comment


                          • #28
                            Hi David,

                            This is because, for a REST request, the authentication is supposed to be done on one URI and should fail on all others if the user is not yet authenticated.

                            The authentication process with the verification of the accreditation.

                            In simpler terms, there is no redirection to an authentication page in a REST context.

                            Comment


                            • #29
                              Here is the blog that described this: http://www.baeldung.com/2011/10/31/s...ty-3-1-part-3/

                              Comment

                              Working...
                              X