Background
I'm using the Spring Security Grails plugin. Because my User and Role classes are not GORM objects, I I've replaced the UserDetailsService provided by the plugin with my own implementation:
class CustomUserDetailsService implements UserDetailsService {
static transactional = false
private static final log = LogFactory.getLog(this)
#Autowired
private UserManager userManager
#Autowired
private RoleManager roleManager
UserDetails loadUserByUsername(String username) {
User user = userManager.getUserByEmail(username)
UserDetails userDetails = new UserAdapter(user, roleManager)
log.debug "user '$username' has roles: ${userDetails.authorities?.authority}"
userDetails
}
}
Problem
When I login, I see the following message is logged from CustomUserDetailsService.loadUserByUsername()
user 'a5511120#nepwk.com' has roles: [USER]
So it seems that the user has been assigned the USER role. However, when I then try and access an action of this controller:
#Secured(['ROLE_USER', 'ROLE_ADMINISTRATOR'])
class MyProfileController {
def someAction = { // impl omitted }
}
I get bounced to the access denied page. I'm pretty sure that the user is logged in, because the access denied page contains markup such as
<sec:ifLoggedIn>
protected content
</sec:ifLoggedIn>
and the protected content is displayed. So it seems that somehow the USER role is not associated with the current user, when the controller authorisation is performed. The log message suggests that the UserDetailsService is OK.
Solution
The solution is to make sure that the role names in the domain class/database begin with "ROLE_", as per the annotation parameters.
Acknowledgements
All credit for this answer goes to #BurtBeckwith and #tim_yates, who provided the solution in comments. I'm converting their comments to an answer, as future readers may easily miss their comments.
Related
I am looking for a sound method of performing input validation for the user credentials on login. Specifically - that the username conforms to some size limit: if provided value is too big, fail fast, instead of passing it through to the userDetailsService.
A somewhat crude solution is to explicitly test this condition at the gate, in the userDetailsService:
#Service
public class MyUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username.length() > MAX_USERNAME_LEN )
throw new BadCredentialsException("username too long");
//...
}
}
From a domain model perspective, the size limit can be expressed as a validation constraint (#Size(max = ...)) on the user entity. Since username is a request parameter, request validation (#Valid) also seems like a possible direction. But I am not sure how to leverage those mechanisms in the current setup.
Can someone provide some guidance on this?
You need to enable bean validation support by registering LocalValidatorFactoryBean and MethodValidationPostProcessor first. (Refer this for the details). If you are using spring-boot , its auto-configuration should already configure them for you.
After that , you can validate the method parameters by using #Validated and #Size:
#Service
#Validated
public class MyUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(#Size(max = MAX_USERNAME_LEN) String username) throws UsernameNotFoundException {
}
}
I want to configure Spring boot and spring security for some rest services.
I have tables User, priviledge, priviledge_user
I have this Configuration files
WebSecurityConfig
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().
httpBasic().and().
csrf().disable();
}
}
and AuthenticationConfiguration
#Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserRepository userRepository;
Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User account = userRepository.findByUsername(username);
log.info("The user ({}) logged has the password {}", username, account.getPassword());
org.springframework.security.core.userdetails.User userSession = new org.springframework.security.core.userdetails.User(account.getUsername(), account.getPassword(), true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
return userSession;
}
};
}
}
debuging the code seems Authentication is working well cause I have the user from database(I am using JNDI datasoure and have this configured in application.properties)
but When I try to get anything on http://localhost:8080/ and set my credentials I cant access and always get the prompt for credentials
Whats I am doing bad?
You are not instantiating the UserDetails object with the correct authorities.
Instead of always passing "USER" to AuthorityUtils.createAuthorityList method, you should pass a List with all the roles/privileges in the privilege_user table for the user you are loading. This roles by default are in the form of "ROLE_USER", "ROLE_ADMIN"...
So loading the User from the database successfully doesn't mean the authentication process is working well. You still have to tell him how to load the authorities for that User.
The problem is, you are using the DB audit with either of #CreatedBy or #LastModifiedBy annotation and yet you are trying to insert a document before any user is logged in. This use to happen, when you create a new user or create operations in a landing page controller. To overcome this, have a system user in your DB, say, system#yourdomain.com then set principal in the corresponding controller if the input params are right.
While doing so, the DBAuditor will get your system user id for the annotated filed. Make sure, you load newly created user principal after this operation.
Playing with Spring Session REST example, I'm curious is it possible to make the following flow:
1. In first time, an user passes its credentials, usename and password.
2. Spring Session will generate a token, puts it into Redis
3. Next time a user asks for some resources it passes its tiken.
My problem is that the following code is hardcoded:
code
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
code`
How can I do it to be dynamic?
Thanks in advance.
Spring Session works independent of the security framework you choose, so the answer depends on which security framework you want to use. This means selecting the username / password for your users is totally independent of Spring Session.
Since the question is unrelated to Spring Session, you should consult the documentation of the security framework you choose. In this instance, it is Spring Security so you can consult Spring Security's documentation on authentication.
The first step is to determine how you want to authenticate.
For example, if you want to use JDBC authentication, you can use something like the following.
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
By default jdbcAuthentication expects that:
select username, password, enabled from users where username = ?
will return the username, the password, and if that user is enabled. You can customize this query if you like using properties on jdbcAuthentication(). See the javadoc for details.
NOTE: It is important to understand that withDefaultSchema and withUser are really only intended for in memory databases since it will try to add the schema and the user every time. In a production environment your users and schema should be added using other mechanisms (i.e. liquibase).
The most flexible option is to implement a UserDetailsService. You can then look up users in any way you want. For example:
#Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
#Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
MyUser user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
List<GrantedAuthority> authorities = convert(user.getRoles());
return new User(user.getUsername(), user.getPassword(), authorities);
}
}
Then you can configure it using:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService);
}
You can find a complete example of using a UserDetailsService in my Spring Security 4 talk.
I'm using Grails v2.4.2 with spring-security-rest, spring-security-core, and spring-security-ui plugins.
I've written a custom UserDetailsService to make the username case-insensitive. All I am doing is simply trying to override
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException
My com.example.core.CaseInsensitiveUserDetailsService class is defined as:
class CaseInsensitiveUserDetailsService extends GormUserDetailsService {
/**
* Make The Username Case Insensitive
*/
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException {
Person.withTransaction { status ->
log.debug "Case Insensitive User Details Service"
// Find The Username
def user = Person.findByUsernameIlike(username)
// If User Not Found, Throw Exception
if (!user) {
log.warn "User not found: $username"
throw new UsernameNotFoundException('User not found', username)
}
Collection<GrantedAuthority> authorities = loadAuthorities(user, username, loadRoles)
createUserDetails user, authorities
}
}
}
My resources.groovy contains:
beans = {
userDetailsService(com.example.core.CaseInsensitiveUserDetailsService)
credentialsExtractor(Grails24CredentialExtractor)
// Some Custom Filters Are Also Defined (securityContextRepository, securityContextPersistenceFilter, multipartResolver)
}
It compiles succesfully, but it never actually runs my custom CaseInsensitiveUserDetailsService. In the console, I see debug statements from the actual GormUserDetailsService instead of my custom one. What can be done to use my custom UserDetailsService?
** Note: I've been following these two tutorials:
http://www.stevideter.com/2012/11/17/case-insensitive-usernames-using-spring-security-core-plugin-for-grails/
http://grails-plugins.github.io/grails-spring-security-core/guide/userDetailsService.html
I had a similar problem when doing something similar with a different plugin and Spring Security, and the wiring needed to be made explicitly to be by name. Try in resources.groovy:
userDetailsService(com.example.core.CaseInsensitiveUserDetailsService) { bean->
bean.autowire = "byName"
}
I wrote this example for educational(self) purposes, perhaps it might help:
https://github.com/grails-coder/grails-spring-security
It regards to:
Authentication Providers
Custom UserDetailsService
Kindly notice it's on top of grails 3.2.5
The authentication for our application happens through siteminder agent but the authorization is controlled through our application.
I am using org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter to check the header. I have also defined UserDetailsService to load the user details.
I also need to set the role for the user so that I can use spring security tag libraries and other spring methods to check the role and show the options.
How do I implement this?
I have tried the below statements in my user details service implementation, but doesn't seem to work.
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, roles);
SecurityContextHolder.getContext().setAuthentication(auth);
I have also read about AbstractPreAuthenticatedProcessingFilter class but looks like this is may not be useful for this purpose.
Any help on this issue will be very helpful.
Thanks!
I was trying to set the roles(using the statements in my question) in the UserDetailsService
implementation and it was not working.
Solution-1:
I have written a sub class PreAuthenticatedAuthenticationProvider and overridden the authenticate method as below :
public class CustomPreAuthenticatedAuthenticationProvider extends PreAuthenticatedAuthenticationProvider {
#Autowired
DBConfig dbConfig;
#Override
public Authentication authenticate(Authentication authentication)throws AuthenticationException {
Authentication auth = super.authenticate(authentication);
User user = (User)auth.getPrincipal();
Set<Role> roles = new HashSet<Role>();
String[] rolesArray = dbConfig.getRoles(user.getAccessLevel());
for(String role: rolesArray){
Role r = new Role();
r.setName(role);
roles.add(r);
}
user.setRoles(roles);
auth = new UsernamePasswordAuthenticationToken(user, null, roles);
return auth;
}
}
Solution-2 :
I tried setting the roles in the controller (the home page after authentication) and it worked. But looks like Solution-1 is a standard solution.