Solved

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

Posted on 2012-03-29
9
1,926 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Salesforce Has Never Been Easier

Improve and reinforce salesforce training & adoption using WalkMe's digital adoption platform. Start saving on costly employee training by creating fast intuitive Walk-Thrus for Salesforce. Claim your Free Account Now

 
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
 

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

Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Google Analytics 5 177
servlet and mdb, jms error 1 79
Java syntax, or is it Selenium 6 57
using executorService 3 16
Browsers only know CSS so your awesome SASS code needs to be translated into normal CSS. Here I'll try to explain what you should aim for in order to take full advantage of SASS.
SASS allows you to treat your CSS code in a more OOP way. Let's have a look on how you can structure your code in order for it to be easily maintained and reused.
The viewer will learn how to use and create new code templates in NetBeans IDE 8.0 for Windows.
HTML5 has deprecated a few of the older ways of showing media as well as offering up a new way to create games and animations. Audio, video, and canvas are just a few of the adjustments made between XHTML and HTML5. As we learned in our last micr…
Suggested Courses

738 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