Announcement Announcement Module
Collapse
No announcement yet.
Combining form-based authentication and REST web services Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Combining form-based authentication and REST web services

    I have a site that uses form-based authentication for login. There are also many RESTful web services available for logged-in users. What is the best way to integrate authentication between the two?

    I would like users to hit the site, login through the standard form, and be able to hit those web services without having to login again, and without having to store credentials in the UI.

    My understanding is that the basic authentication mechanism is stateless, which would require me to send a username and password for every call. Is this correct? My thought was that there could be some kind of token such that, after initial login, I could just send the token as part of the REST call so that the app could use it as a reference for the already-logged-in principal.

    My other thought was that I could simply perform login in the Java code by injecting the AuthorizationProvider, and accessing the SecuriyContext via the SecurityContextHolder. This is effectively what I do for my integration tests, but it just feels wrong.

    The only relevent thread I've found was this:

    http://forum.springsource.org/showth...highlight=rest

    It's different in that this poster seems to want two separate authentications...I'm simply trying to get REST web services to acknowledge that a user has already logged in via a form.

  • #2
    So currently I'm going about it as a vanilla java method. Looks something like this:

    Code:
    @Autowired
    AuthenticationProvider provider;
    
    void authenticate(String username, String password) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(username, password);
        provider.authenticate(authentication);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    The Authentication object is set, and is available elsewhere in the app, but it doesn't have an UserDetails object or GrantedAuthorities. The AuthenticationProvider is just the Spring Security default.

    My custom UserDetailsImpl.loadUserByUsername() is firing on provider.authenticate() and returning a populated UserDetails object, but it does not seem to be associated with the Authentication object anywhere.

    I could just set it on the Authentication object, but the more I manually do, the more I feel like I'm doing things in a non-standard (bad) way.

    As I am only running tests at this point, I fear this won't work with stateless HTTP requests.

    Comment


    • #3
      Oddly, even without setting the UserDetails or being able to get at them in the Authentication object returned by context.getAuthentication(), JSR250 annotations on secured methods are still working correctly.

      Comment


      • #4
        Well, I figured an ounce of this out. The Authentication object passed into the AuthenticationProvider is not populated...A fully populated Authentication object is returned by AuthenticationProvider.authenticate(). That line of code should be:

        Code:
        authentication = provider.authenticate(authentication);

        Comment


        • #5
          Wow, not a lot of love for web services and Spring Security...

          What do you guys use?

          Comment


          • #6
            I think at a high-level you seem to have it right. You'll probably want to put the token you're passing around in the header instead of the url string.

            I don't see why you can't use spring security normally with intercept-url definitions. But I'm not an expert around these parts so dont take my word for it.

            Comment


            • #7
              I'm confused at what exactly you're trying to do, possibly because you're confusing terminology. You're saying REST Web Services, and then you are saying that users should be able to use them while they're logged in. Are these REST calls being made from the client browser, or from a standalone web service client?

              If they're being made from the client browser w/XHR/Javascript, you really don't need to do anything beyond securing the URLs with the standard login form and ensuring that your Javascript code correctly handles a 302 redirect if the user hasn't authenticated yet.

              If they're being made from a standalone web services client, it will require a different authentication strategy.

              Let us know how these services are being called and we can try to help!

              Comment


              • #8
                Gotcha. We have a standard web site, and it will be secured via <http>, autoconfigged in addition to JSR250 method security. Many of our pages are dynamic, and we make a lot of asynchronous calls to RESTful web services through Javascript. AJAX, JSON, jQuery...Looks something like this:

                Code:
                        $.ajax({
                            type : "POST",
                            data : {"key":"value"},
                            url : '/rest/user/secure-info',
                            contentType : "application/json; charset=utf-8",
                            dataType : "json",
                            success : function(data) {
                                // yay
                            }
                        });
                So the login box is drawn somewhere. It sends an ajax call to a web service over HTTP, then it updates the box based on the return (success/failure/missing param, etc).

                The web service is just a java class that does the authentication as I've described above. I think it's pretty much working, but it hasn't been tested extensively.

                I'm mainly afraid of losing the principal stored in ThreadLocal. I'm unsure how it will behave via (seemingly) unrelated HTTP requests.

                Comment


                • #9
                  Unless you've configured things otherwise, the authentication information is stored in the user's HTTP session, which would be shared by AJAX and non-AJAX calls. The use of a ThreadLocal is only done once a security context has been restored from the session by the first filter in the Spring Sec servlet chain.

                  Comment


                  • #10
                    This is good to know.

                    If I'm doing the login and authentication in my own Java (as above) and not through a servlet filter...How do I get that Authentication in to the HTTP session?

                    Comment


                    • #11
                      I guess I would suggest going about it a different way - I would suggest having the authentication for the REST service occur within the Spring Security filter context too. If you do this, you shouldn't need to do anything special to authenticate within the REST service, because Spring will do it for you. You'd just have to add appropriate access declarations to the set of <intercept-url> rules you already have. The Spring Sec filter chain that wraps your REST requests will verify the authentication context in the session is correct well before your servlet code is invoked, and it will ensure that things are set up so that your method annotations still work properly.

                      Comment


                      • #12
                        Hi,
                        I am very interested in the correct solution to this problem - I would like to implement form-based authentication in a declarative way, without using the ServletContextHolder directly.

                        If you find a solution please post it here!

                        Many thanks,
                        Michele

                        Comment


                        • #13
                          It looks like we're probably going to end up passing the jsessionid in as a parameter when making RESTful calls. We will also pass in the encoded auth token.

                          So a user hits a page, they get a jsessionid, and we just pass it in for web services. If they're logged in, we pass an auth token, too.

                          Comment


                          • #14
                            Actually, when a user hits a page, we store an anonymous user ID (a Java UUID) in a cookie. I pull the cookie from the REST class and get the ID from there. We're using that to identify which user is which.

                            I'm not sure about actual authentication yet. It'll probably be a hashed token stored in a cookie.

                            Comment


                            • #15
                              Ok, I am trying to implement something similar. I have just started with spring and rest services.. .if you add how you have implemented it, it would be great.

                              Comment

                              Working...
                              X