Spring Storage Loading Users into a Database with Grails 3.0
How do you store users in a database with a new simple Vanilla Grails 3.0 application?
Background:
- Shiro and Spring security plugins are not yet available for Grails 3.0 (and it looks like Spring Boot is the future for Grails security).
- There are various examples to show how to use it
inMemoryAuthentication()
, but they seem completely pointless because ultimately passwords are stored in plain text (and it only takes 30 seconds to create a domain model in Grails). - Almost all Grails applications require this feature.
- I am using MongoDB, but it probably doesn't matter.
- Related: Graphics 3 and Spring Security Plugin
I inMemoryAuthentication()
currently have the following working:
build.gradle
compile "org.springframework.boot:spring-boot-starter-security"
Grails app / conf / spring / resources.groovy
import com.tincanworks.AppSecurityConfig beans = { webSecurityConfiguration(AppSecurityConfig) }
AppSecurityConfig.groovy
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/assets/**").permitAll()
.antMatchers("/admin/**").hasAnyRole("admin")
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll()
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("123456").roles("user")
.and()
.withUser("admin").password("1qaz2wsx").roles("user", "admin")
}
}
The answer seems to be related to JdbcDaoImpl , but I have no idea how to hook this up in Grails.
source to share
Gorm based
I wrote two blog posts ( Part 1 - In Memory Auth and Part 2 - Gorm Based Authentication on how to use spring-starter-security and GORM in a Grails 3 application. I also created a github repo with a working Grails 3 application using spring-starter-security.
JDBC-unverified
Alternatively, if you want to use standard JDBC based authentication, you can simply create the database tables using the following SQL script
HSQLDB
From http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-schema.html
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(50) not null,
enabled boolean not null);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
MySQL
This comes from http://justinrodenbostel.com/2014/05/30/part-5-integrating-spring-security-with-spring-boot-web/
create table users (
username varchar(50) not null primary key,
password varchar(255) not null,
enabled boolean not null) engine = InnoDb;
create table authorities (
username varchar(50) not null,
authority varchar(50) not null,
foreign key (username) references users (username),
unique index authorities_idx_1 (username, authority)) engine = InnoDb;
and then change the method configureGlobal
to
@Autowired //not sure if this is needed as you have the AppSecurityConfig bean referenced in resources.groovy
def datasource //bean injected by Grails
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(datasource)
}
source to share
If you want to avoid creating an entire user management layer from scratch with a DB, you might want to consider Stormpath .
Among other things, they provide a Spring security plugin that uses Stormpath as its authentication and authorization provider. They also have a sample Spring Security Application that shows how the plugin is used. Since you are using Java Annotations (not xml config) take a look at this thread .
So, in a nutshell, the key snippets you need to identify are as follows:
-
Stormpath Bean client that provides fast and secure communication with Stormpath using the Stormpath Java SDK :
//Let create the Stormpath client using the apiKey.properties file from the User home folder. @Bean ClientFactory stormpathClient(CacheManager cacheManager) { ClientFactory clientFactory = new ClientFactory(); clientFactory.setApiKeyFileLocation(System.getProperty("user.home") + File.separator + ".stormpath" + File.separator + "apiKey.properties"); clientFactory.setCacheManager(cacheManager); return clientFactory; }
-
You will need to define a Stormpath authentication provider so that Spring Security can transparently interact with the Stormpath to authenticate and authorize users:
@Bean @Autowired public StormpathAuthenticationProvider stormpathAuthenticationProvider(Client client, String applicationRestUrl) throws Exception { StormpathAuthenticationProvider stormpathAuthenticationProvider = new StormpathAuthenticationProvider(); stormpathAuthenticationProvider.setClient(client); stormpathAuthenticationProvider.setApplicationRestUrl(applicationRestUrl); return stormpathAuthenticationProvider; }
-
applicationRestUrl
should point to the Stormpath application where all users / groups will exist:@Bean public String getApplicationRestUrl() { return "https://api.stormpath.com/v1/applications/9TqbyZ2po73eDP4gYo2H92"; }
-
Spring security configuration must be configured to use the Stormpath authentication provider:
//Let add the StormpathAuthenticationProvider to the `AuthenticationProvider` @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(stormpathAuthenticationProvider); }
-
Finally, to restrict access to resources to roles, you will need to define roles. For example:
//The access control settings are defined here @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin() .and() .authorizeRequests() .accessDecisionManager(accessDecisionManager()) .antMatchers("/account/*").hasAuthority("https://api.stormpath.com/v1/groups/36O9eBTN2oLtjoMSWLdnwL") //you are giving access to "/account/*" to users' that belong to the group univocally identified by this href value .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/index.jsp") .and() .httpBasic() .and() .csrf().disable(); }
Disclaimer, I am an active member of Stormpath.
source to share