Spring Security and Custom Authentication Provider - java

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
}
}

Related

Conditional auth-provider in auth-manager

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.

Spring Security - get current user fields from database

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.

Spring security UserDetailsService configuration

I added a custom UserDetails and UserDetailsService class to my spring project and want to use them in combination with httpbasic. How can I configure spring to use my custom classes?
My application.yml looks like this:
security:
basic:
enabled: false
require_ssl: false
enable_csrf: false
ignored:
- /register/**
- /acitivate/**
headers:
hsts: domain
sessions: stateless
UserDetailsService:
#Service
#Transactional(readOnly = true)
public class UserDetailsServiceAdapter implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//...
}
}
Do I have to define Beans, or is it possible to set it within my application.yml?
You do not have to define the bean for the "UserDetails" in your XML. The fact that your config file is in YAML does not make things any different.
so in your case, you can derive from it (extend it) and it will be OK. Remember to call "super.loadUserByUsername()" in your impl, and in the class where you use it you will have to down-cast.

Using jdbc-user-service inside user-service-ref

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.

SpringSecurity - Custom automatic authentication

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!

Categories

Resources