I am using spring security and for database based authentication I used following configuration and its working fine
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="SELECT ...... U.email_address=?"
authorities-by-username-query="SELECT ... U.email_address=?">
</authentication-provider>
</authentication-manager>
now I wanted to add extra info to the session and came across Adding user to session, spring security default login, I tried it and now I have a problem.
XML says I cant use user-service-ref and jdbc-user-service combined. Is there a way to sort it out or else what I have to do if I have to use user-service-ref tag only to authenticate users? What can be the other way to add extra info say a whole Users object to the session.?
Your help will be appreciated.
After hours of searching and experimenting I was able to do it like this.
Make a new service say MyUserService that will implement org.springframework.security.core.userdetails.UserDetailsService and has annotation #Service. UserDetailsService only has one method loadUserByUserName. Implementation of this method will be in MyUserService. It will look like this.
#Service
public class MyUserService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String arg0)
throws UsernameNotFoundException {
MyUser user=new MyUser();
/*get details of user and authorities from database whose username is
arg0 and place them in user instance */
return user;
}
}
MyUser is also a new class that implements org.springframework.security.core.userdetails.UserDetails and all its methods are implemented inside MyUser class. It will look like this.
public class MyUser implements UserDetails {
private static final long serialVersionUID = 1L;
/*All the variables their getter setters that you wish to store in session. And
implementation of all the methods of UserDetails go here.*/
}
Define a bean like this
<bean id="customUserDetailsService" class="org.aurora.timeexpense.service.MyUserService"/>
Where org.aurora.timeexpense.service.MyUserService is the path of my defined service that implements UserDetailsService.
4.And Spring Security Configuration will go like this
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailsService">
</authentication-provider>
</authentication-manager>
You are good to go.
Related
I have security configuration via xml. I've added an extra provider. So it looks like this:
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="first" />
<sec:authentication-provider ref="second">
</sec:authentication-provider>
</sec:authentication-manager>
I would like to either use both providers or just one, based on an entry in an app.properties file. Is it even possible ?
Probably the easiest way is to have each AuthenticationProvider answer for itself. There is a supports method that you can implement or there is the option of returning null from authenticate, which indicates that this AuthenticationProvider is abstaining.
Overriding supports
public class FirstAuthenticationProvider implements AuthenticationProvider {
#Value("${myapp.security.usefirst}") boolean useFirst;
#Override
public boolean supports(Class<?> authentication) {
return useFirst && anyOtherTestingNecessary;
}
}
AuthenticationProvider abstains
public class SecondAuthenticationProvider implements AuthenticationProvider {
#Value("${myapp.security.usefirst}") boolean useFirst;
#Override
public Authentication authenticate(Authentication authentication) {
if ( useFirst ) {
return null; // abstain
}
// ... rest of authentication strategy
}
}
If you are using Spring 4 or higher, then you can use the #Conditional annotation to wire each provider conditionally based on your property, though based on your question, I'll forgo that example.
You can, of course, as suggested in a comment, create a wrapper AuthenticationProvider, though this can cause some heartburn depending on your wiring strategy. Also, it is duplicating a little bit of the work that ProviderManager is already intended to do.
I have a table in my database which contains user information.
CREATE TABLE users
(
id_user serial NOT NULL,
phone text,
password text,
balance numeric,
email text,
CONSTRAINT users_pkey PRIMARY KEY (id_user),
CONSTRAINT users_login_key UNIQUE (phone)
)
I use Spring Security. Part of the config:
<security:jdbc-user-service id="userService"
data-source-ref="dataSource"
users-by-username-query="select phone, password, true from users where phone=?"
authorities-by-username-query="select phone,'ROLE_USER' from users where phone=?" />
How can I get, for example, current user balance in my Spring MVC Controller?
The JDBC service you defined in the Spring Security configuration will only fetch username, password and enabled status using the users-by-username-query query and username's authorities using the authorities-by-username-query query. This means that any extra attributes will not be mapped to the user principal object which will be created to populate the security context.
One way to get all information of your user is to create custom implementation of UserDetailsService which will be able to retrieve user data including all of its custom attributes. Here is an example of this approach (it assumes that you are using JPA/Hibernate for your data layer):
Entity
#Entity
#Table(name="users")
public class User implements UserDetails {
private BigDecimal balance;
// Other properties omitted ...
// Getters and setters omitted ...
// Implementation of UserDetails methods omitted ...
}
UserService
Service responsible for loading the user information. Note that in real world you should probably have separate DAO querying users, program against interfaces, etc. This example is deliberately kept as short as possible.
#Service
public class UserService implements UserDetailsService {
#Autowired
private SessionFactory sessionFactory;
#Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
Query query = sessionFactory.getCurrentSession().createQuery("FROM User u WHERE u.username = :username");
query.setParameter("username", username);
User user = (User) query.uniqueResult();
if (user == null) {
throw new UsernameNotFoundException("User with username '" + username + "' does not exist.");
}
return user;
}
}
Configuration
Create AuthenticationProvider bean and supply your custom user service as its property.
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userService" />
<!-- Other required properties ... -->
</bean>
Controller
You can access the current user and its properties in the controller using:
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
BigDecimal balance = user.getBalance();
As a final remark, please note that this is not 100% working copy and paste solution to your problem, rather a demonstration of the approach you can take to achive the desired result. I recommend looking through the documentation of core classes/interfaces such as AuthenticationProvider, UserDetailsService, UserDetails, and Spring Security guides for more details.
This is my scenario:
a web-app perform a sort-of SSO for many applications
logged-in user than click on a link and the app makes a post with user informations (name, pwd [useless], roles) toward the proper application
I am implementing SpringSecurity on one of these application to benefit from its power (authorities in session, methods provided by its classes, etc)
So, I need to develop a custom filter - I guess - that is able to retrieve user informations from request, retrieve from database, through a custom DetailsUserService, further information about the user (email, etc...) and then perform authentication of that user, according to the role retrieved from the request.
I was looking at Pre-Authentication filters, but I'm not sure that it is the right choice. It seems that those object are expected to be used when the principal is already in session, put by some previous authentication machanism (is it right?).
I think that, once identified the correct filter, I should need to perform within something like:
GrantedAuthority[] ga= new GrantedAuthority[1];
ga[0] = new GrantedAuthorityImpl(myUser.getRole());
SecurityContext sc = SecurityContextHolder.getContext();
Authentication a = new UsernamePasswordAuthenticationToken(userName, userPwd, ga);
a = authenticationManager.authenticate(a);
sc.setAuthentication(a);
Is it the proper direction to solve my problem? Do you have suggestions to help me find what's missing?
Thank you all,
Luca
ADDITION:
Hi Xearxess! Sorry to bother you again but it seems that the translation of your code according to SpringSecurity 2.0.4 is more difficult than I thought :S The problem is the XML... I tried different configuration but I ran always into namespace problems, missing attributes, etc...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
<security:http>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:logout logout-url="/logout" logout-success-url="http://milan-ias-vs.usersad.everis.int/DMTest/" invalidate-session="true" />
<security:custom-filter position="PRE_AUTH_FILTER" ref="preAuthenticatedProcessingFilter" />
</security:http>
<bean id="preAuthenticatedProcessingFilter" class="it.novartis.ram.authentication.PreAuthenticatedProcessingFilter">
<custom-filter position="PRE_AUTH_FILTER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean class="it.novartis.ram.authentication.PreAuthenticatedUserDetailsService" />
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
</beans>
The 2 rows referencing CUSTOM-FILTER element are two different tries, both of them signed as error. How can I specify the position of my filter as a property?
Also the authentication provider reference on auth manager definition is marked as error. I think that I need to specify it like a property too, right?
Hope you can give me the last push ;)
Thank you again,
Luca
For sake of completeness, in Spring Security 4 things are slightly changed. For example, the Java configuration is highly recommended. In this way, it's easier to integrate with Spring Boot.
It follows the Java Configuration that is equivalent to the XML configuration given in the above answers.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(customAuthFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.authenticationProvider(preauthAuthProvider())
.authorizeRequests()
.anyRequest().authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preauthAuthProvider());
}
#Bean
public PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
PreAuthenticatedAuthenticationProvider preauthAuthProvider =
new PreAuthenticatedAuthenticationProvider();
preauthAuthProvider.setPreAuthenticatedUserDetailsService(
userDetailsServiceWrapper());
return preauthAuthProvider;
}
#Bean
public OnlyRolesPreAuthenticatedUserDetailsService userDetailsServiceWrapper() {
OnlyRolesPreAuthenticatedUserDetailsService service =
new MyPreAuthenticatedUserDetailsService();
return service;
}
#Bean
public MyPreAuthenticatedProcessingFilter customAuthFilter() throws Exception {
MyPreAuthenticatedProcessingFilter filter = new MyPreAuthenticatedProcessingFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
I think that the above code is worth, because examples in internet are very basic and the Spring documentation lacks of such details.
Yes, Pre-Authentication Scenarios are exactly what you are looking for.
It seems that those object are expected to be used when the principal
is already in session, put by some previous authentication machanism
(is it right?).
Not really, you can use Pre-Authentication to create PreAuthenticatedAuthenticationToken from request, as you want. Just do few things I described in another question.
First extend AbstractPreAuthenticatedProcessingFilter to obtain username and roles from request:
public class MyPreAuthenticatedProcessingFilter
extends AbstractPreAuthenticatedProcessingFilter {
public MyPreAuthenticatedProcessingFilter(
AuthenticationManager authenticationManager) {
setAuthenticationDetailsSource(new MyAuthenticationDetailsSource());
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return "Anonymous";
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
public static class MyAuthenticationDetailsSource implements
AuthenticationDetailsSource<HttpServletRequest, MySessionUserDetails> {
// roles probably should be encrypted somehow
static final String ROLES_PARAMETER = "pre_auth_roles";
#Override
public MySessionUserDetails buildDetails(HttpServletRequest req) {
// create container for pre-auth data
return new MySessionUserDetails(req.getParameter(ROLES_PARAMETER));
}
}
}
MySessionUserDetails class will split spring with roles to List of SimpleGrantedAuthority or any other GrantedAuthority implementation. Also, List is recommended and superior to GrantedAuthority[].
Second, implement AuthenticationUserDetailsService:
public class MyPreAuthenticatedUserDetailsService implements
AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
#Override
public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)
throws UsernameNotFoundException {
MySessionUserDetails sessionUserDetails =
(MySessionUserDetails) token.getDetails();
List<GrantedAuthority> authorities = sessionUserDetails.getAuthorities();
return new User(token.getName(), "N/A", true, true, true, true, authorities);
}
}
Then in your XML connect blocks together:
<security:http use-expressions="true">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:custom-filter position="PRE_AUTH_FILTER"
ref="myPreAuthenticationFilter" />
</security:http>
<bean id="myPreAuthenticationFilter"
class="com.example.MyPreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean class="com.example.MyPreAuthenticatedUserDetailsService" />
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
And voila! You should have authenticated User principal to use in your application.
Code I written here requires Spring Security 3.1 which I strongly recommend if you're about to using it (it does requrire Spring 3.0.7+). Also, Spring Security reference manual is your friend!
I have a custom AuthenticationProvider that simply returns the Authentication object for the authenticate method. What I want to do is add a role to the user when they log in. This is for demo purposes, so all I want is the user to enter a username and let them in. I need to assign them the admin role.
There are, of course, several ways to achieve that.
My preferred one is do this in a custom UserDetailsService. The only method is loadUserByUsername, that will return an instance of UserDetails. When you are constructing your UserDetails, you can add whatever GrantedAuthority you want.
So first, you'll declare your custom UserDetailsService in your application context configuration file:
<bean id="myCustomUDS" class="com.myapp.AppUDS" />
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="myCustomUDS">
</sec:authentication-provider>
</sec:authentication-manager>
Then you write the class itself:
public class AppUDS implements UserDetailsService {
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
//create your concrete UserDetails
//add your custom role (i.e. GrantedAuthority) to that object (that will be added to all users)
//return it
}
}
Typically, when you declare different "<authentication-provider>" for your application (webapp in my case), Spring Security takes care of invoking providers one after another, incase of failure. So, say I have DatabaseAuthenticationProvider and LDAPAuthenticationProvider with DatabaseAuthenticationProvider declared first in the config file, at runtime, DatabaseAuthenticationProvider is invoked first and if authentication fails, LDAPAuthentication is tried. This is cool - However, what I need is a runtime switch.
I would like to have an option of chosing between these two approaches (database based authentication / ldap based authentication) and somehow swith the implementation based on thsi global setting.
How do I do it? Is it even possible with Spring-Security?
I will leave how to inject your own custom authentication provider to the other myriad of examples from Googleland and here on StackOverflow. It looks like it has to do with marking a particular bean with the xml. But hopefully I can fill in some of the other details for you.
So you've defined the class somewhat like above and I'll add more of the details that you'll need for Spring (i.e. merge the stuff from above as well.
public class SwitchingAuthenticationProvider implements AuthenticationProvider
{
....
public List<AuthenticationProvider> getProviders() { return delegateList; }
public void setProviders(List<AuthenticationProvider> providers) {
this.delegateList = providers;
}
....
}
This will allow you to inject a host of providers using spring:
<bean id="customAuthProvider1" class=".....CustomProvider1"> ... </bean>
<bean id="customAuthProvider2" class=".....CustomProvider2"> ... </bean>
...
<bean id="customAuthProviderX" class=".....CustomProviderX"> ... </bean>
<bean id="authenticationProvider" class="....SwitchingAuthenticationProvider">
<security:custom-authentication-provider/>
<!-- using property injection (get/setProviders) in the bean class -->
<property name="providers">
<list>
<ref local="customAuthProvider1"/> <!-- Ref of 1st authenticator -->
<ref local="customAuthProvider2"/> <!-- Ref of 2nd authenticator -->
...
<ref local="customAuthProviderX"/> <!-- and so on for more -->
</list>
</property>
</bean>
In the end how you populate the providers could be any means of getting the delegator a collection of providers. How they map up to which one to use is up to you. The collection could be a named mapped, based on the current state of the delegator. It could be a list of more than one to try. It could be two properties, "get/setPrimary" and "get/setSecondary" for fail-over like functionality. Once you have the delegator injected the possibilities are up to you.
Let me know if this isn't answering your question.
How about writing a delegating AuthenticationProvider that knows how to access your runtime switch and the actual instances of Database/LDAP AuthenticationProvider.
I'm thinking of something like:
public class SwitchingAuthenticationProvider implements AuthenticationProvider
{
private List<AuthenticationProvider> delegateList;
private int selectedProvider;
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
{
AuthenticationProvider delegateTo = delegateList.get(selectedProvider);
return delegateTo.authenticate(authentication);
}
....
}