Solved

Spring security - how to extend userDetailsService to add an additional field id

Posted on 2012-03-29
9
1,754 Views
Last Modified: 2013-11-23
I am using the following security_context.xml in a new spring framework that we are trying to set up
For authentication we are using

<authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="rod" password="koala" authorities="supervisor, teller, user" />
                <user name="dianne" password="emu" authorities="teller, user" />
                <user name="scott" password="wombat" authorities="user" />
                <user name="peter" password="opal" authorities="user" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
we will eventually use LDAP but for now planning to use a properties file and utilize <user-service id="userDetailsService" properties="users.properties"/>

In addition to username, password, grantedAuthority, enable/disable we would like to add userid as a field. How to extend  userDetailsService to add this extra attribute any pointers will be greatly appreciated.
0
Comment
Question by:softprossg
  • 4
  • 4
9 Comments
 
LVL 35

Expert Comment

by:mccarl
ID: 37794467
Ok, so to do what you are after will require a number of steps...

1) You will need to extend Spring's UserDetails interface, to add a method for retrieving the "userId" field.

2) You will need to create your own implementation of the UserDetailsService interface. This will take as a property (or constructor arg) the properties file that contains your user info. It will also most likely load that property file on startup and store the details in an in-memory data structure such as a Map object. The UserDetailsService interface only has one method, so in your implementation of that method, you will look up the username provided and return the details in the file. These details will be in an object that you create which implements the interface you created in step 1 above. You could take the InMemoryDaoImpl source code as a possible example of how to do this, although I would make it a bit simpler than what it does.

3) You will need to register your custom UserDetailsService implementation for Spring Security to use, with some XML something like the below...
  <authentication-manager>
    <authentication-provider user-service-ref='myUserDetailsService'/>
  </authentication-manager>

  <beans:bean id="myUserDetailsService"
      class="package.name.CustomPropertiesUserDetailsServiceImpl">
    <beans:property name="properties" value="users.properties"/>
  </beans:bean>

Open in new window



4) To use the above, whereever you access the current UserDetails object, you will just have to check that it is an "instanceof" you interface created in Step 1, and then cast it as that interface and then you will be able to access your userId field.



At a later time, when you look to use LDAP, you would look at implementing the UserDetailsContextMapper interface which maps from LDAP info to your custom UserDetails created in Step 1 above, and then you can set that UserDetailsContextMapper on the LdapAuthenticationProvider that you would be using.



Let us know if you need more detailed information in this regard, and I can help further.
0
 

Author Comment

by:softprossg
ID: 37804058
thanks mccarl, I am working on it.
0
 

Author Comment

by:softprossg
ID: 37809429
hi mccarl

I have customized userDetailsService, my security context :

<beans:bean id="webDetailsService" class="com.lmco.fs21.security.WebUserDetailsService">
   </beans:bean>
<beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>
    <authentication-manager>
        <authentication-provider  user-service-ref="webDetailsService">
        <password-encoder ref="encoder" />          
        </authentication-provider>
    </authentication-manager>


My impl also works

public class WebUserDetailsService implements UserDetailsService{
      private static final Logger LOGGER = Logger.getLogger(WebUserDetailsService.class);
   
      @Override
      public UserDetails loadUserByUsername(String username)
                  throws UsernameNotFoundException {
            final ArrayList<GrantedAuthority> ROLES = new ArrayList<GrantedAuthority>();
            ROLES.add(new GrantedAuthorityImpl("supervisor"));
        //return new WebUserDetails("rod", "koala", true, true, true, true, ROLES, "01");
            return new WebUserDetails("rod", "13e778ebbddf10b56326871a83d2ee03a1b675e03fca2b63e99577671f536d83bec68954dfb95a5d", true, true, true, true, ROLES, "01");
      }
      
      
}

but I am not being able to load the properties file in my code as you suggested

<beans:bean id="webDetailsService" class="com.lmco.fs21.security.WebUserDetailsService">
                   <beans:property name="properties" value="users.properties" />
             </beans:bean>       
not sure how to reference the property file in my code.

Please can you guide me.
0
 
LVL 35

Expert Comment

by:mccarl
ID: 37809519
Ok, so in your WebUserDetailsService class, have it implement InitializingBean aswell, this will give you an afterPropertiesSet method that gets called by Spring after it has instantiated your 'webDetailsService' bean. Also, add a private member to hold the value 'properties' property (as a String) and a setter method so that Spring can inject the "user.properties" string from the XML configuration.

And then in your afterPropertiesSet method, you load the properties file with code something like below...

Properties props = new Properties();
props.load(this.getClass().getResourceAsStream(propertiesStringValue));

Open in new window


And then you can iterate through the entries in the 'props' object (it implementes Map, so that should be easy) and parse whatever format you want to use for the file. Of course, if you aren't worried about having that file be formatted as a true .properties file, nothing is tied to that. So you could, for example, load a file that is in CSV format and parse just as you might do in any other program. Its up to you.

Anyway, the result is that from the above parsing, you should create/populate a Map<String, WebUserDetails> object, with the username as the key and the created WebUserDetails objects as values, and then your loadUserByUsername method would be as simple as...

@Override
      public UserDetails loadUserByUsername(String username)
                  throws UsernameNotFoundException {
            WebUserDetails userDetails = userDetailsMap.get(username);
            if (userDetails == null)
                  throw new UsernameNotFoundException();
            return userDetails;
      }

Open in new window


Hope that is enough to guide to the solution you are after!
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 

Author Comment

by:softprossg
ID: 37844294
Dear Mccarl

Thanks for you excellent suggestions, I am past userDetails hurdle, now teying to set up a customized login page with

<form-login login-page="/login" default-target-url="/success"
                  always-use-default-target="true" login-processing-url="/j_spring_security_check" authentication-failure-url="/login?error=1" />

I could set up the controller and it works fine for the first time I start the server. From second instance after a logout, cannot login back with the same user. Apparently the logout is not clearing the server cache. It is not browser cache, can you suggest me once again what needs to be done.

Thanks for all your help.
0
 
LVL 35

Accepted Solution

by:
mccarl earned 500 total points
ID: 37845330
In the <http/> element of your configuration, have you turned off the 'auto-config' attribute? ie. set it to false? If so, you will need to add back in a child element called <logout/>. And you are logging out by accessing the /j_spring_security_logout URL?

If the above isn't the case, then you will need to get a bit more information because it isn't clear what is happening otherwise. By 'cannot login back with the same user', what exactly do you mean? Does it fail with an error message? Or is it just as though that user is no longer an authorized user? Can you post your full config?

Also, try it WITHOUT your custom UserDetailsService again, just the standard static users in the configuration as per your original post, just to make sure that there isn't an issue there.
0
 

Author Comment

by:softprossg
ID: 37900382
thanks Mccarl I re did the application from scratch, now logout is working with custom user details.
0
 
LVL 35

Expert Comment

by:mccarl
ID: 37900413
Glad you were able to resolve your issues!
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Suggested Solutions

Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now