Spring Security | Authentication | UserDetailsService: Searching User Information
The role of searching for user information is handled by UserDetailsService. Spring Security includes several classes that implement UserDetailsService.
Using Memory
This implements storing user information in memory. The concrete class is InMemoryUserDetailsManager.
XML Configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...>
...
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user name="hoge" password="HOGE" authorities="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
- Declare it by using the
<user-service>and<user>tags.
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
...
}
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hoge").password("HOGE").roles("USER");
}
}
- Declare a method that receives AuthenticationManagerBuilder in the configuration class and add the
@Autowiredannotation. - Configure the definition information with the inMemoryAuthentication() method.
JDBC
This implements retrieving user information from a database. The actual class is JdbcUserDetailsManager.
build.gradle (additional dependencies)
compile 'org.springframework:spring-jdbc:4.3.6.RELEASE'
compile 'com.h2database:h2:1.4.193'
- H2 is added and used as the database for verification.
- spring-jdbc is also added as a dependency to create a DataSource.
src/main/resources/sql/create_database.sql
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),
CONSTRAINT UK_AUTHORITIES UNIQUE (USERNAME, AUTHORITY)
);
INSERT INTO USERS VALUES ('fuga', 'FUGA', true);
INSERT INTO AUTHORITIES VALUES ('fuga', 'USER');
- If you declare the table columns as written above, user information is retrieved automatically by default.
- This can also be changed depending on the configuration.
- Place the file under the classpath. It will be used later as the initialization script when creating the data source.
XML Configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
...
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
...
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:/sql/create_database.sql" />
</jdbc:embedded-database>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:jdbc-user-service data-source-ref="dataSource" />
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
- Use the
<jdbc-user-service>tag. - The data source definition runs the initialization script written above by using the
<jdbc:script>tag.
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import javax.sql.DataSource;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
...
}
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("/sql/create_database.sql")
.build();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
- Use the jdbcAuthentication() method of AuthenticationManagerBuilder.
Changing Table and Column Names
CREATE TABLE USERS (
LOGIN_ID VARCHAR_IGNORECASE(50) NOT NULL PRIMARY KEY,
PASSWORD VARCHAR_IGNORECASE(50) NOT NULL,
ENABLED BOOLEAN NOT NULL
);
CREATE TABLE AUTHORITIES (
LOGIN_ID VARCHAR_IGNORECASE(50) NOT NULL,
ROLE VARCHAR_IGNORECASE(50) NOT NULL,
CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY (LOGIN_ID) REFERENCES USERS(LOGIN_ID),
CONSTRAINT UK_AUTHORITIES UNIQUE (LOGIN_ID, ROLE)
);
-
Change USERNAME to LOGIN_ID and AUTHORITY to ROLE.
-
To change table names and column names to whatever you want, change the SQL used to search user information.
-
As long as the order of the selected items matches during retrieval, the column names can be anything.
XML Configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
...>
...
<sec:authentication-manager>
<sec:authentication-provider>
<sec:jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="SELECT LOGIN_ID, PASSWORD, ENABLED FROM USERS WHERE LOGIN_ID=?"
authorities-by-username-query="SELECT LOGIN_ID, ROLE FROM AUTHORITIES WHERE LOGIN_ID=?" />
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
- Define a query that retrieves USERNAME, PASSWORD, and ENABLED with the users-by-username-query attribute.
- Define a query that retrieves USERNAME and AUTHORITY with the authorities-by-username-query attribute.
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import javax.sql.DataSource;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
...
}
@Bean
public DataSource dataSource() {
...
}
@Autowired
public void configure(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("SELECT LOGIN_ID, PASSWORD, ENABLED FROM USERS WHERE LOGIN_ID=?")
.authoritiesByUsernameQuery("SELECT LOGIN_ID, ROLE FROM AUTHORITIES WHERE LOGIN_ID=?");
}
}
- Define a query that retrieves USERNAME, PASSWORD, and ENABLED with the usersByUsernameQuery(String) method.
- Define a query that retrieves USERNAME and AUTHORITY with the authoritiesByUsernameQuery(String) method.
Creating UserDetailsService
MyUserDetailsService.java
package sample.spring.security;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if ("hoge".equals(username)) {
return new User(username, "HOGE", AuthorityUtils.createAuthorityList("USER"));
}
throw new UsernameNotFoundException("not found : " + username);
}
}
- Create a class that implements UserDetailsService.
- UserDetailsService has only one method, loadUserByUsername(String).
- A string that identifies the user, usually the username entered on a login screen, is passed as an argument, so return the user information corresponding to that identifier.
- If there is no matching user information, throw UsernameNotFoundException.
- User information is written as a class that implements the UserDetails interface.
- A standard class named User is provided.
- If you need to change the User class for some reason, create a class that implements the UserDetails interface.