How to add new user to Spring Security at runtime
Solution 1
You probably want to store your users in a database and not in memory, if they are registering :)
-
Create the authorities for the user
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
-
Instantiate the user (with a class implementing UserDetails)
UserDetails user = new User("[email protected]", passwordEncoder.encode("s3cr3t"), authorities);
-
Save the user somewhere useful. The JdbcUserDetailsManager can save a user to a database easily.
userDetailsManager.createUser(user);
-
Create a UsernamePasswordAuthenticationToken
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
-
Add the Authentication to the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
Solution 2
You can use Spring Data JPA for user creation.
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
usage:
User user = new User();
userRepository.save(user);
How to authenticate above user:
- Create custom
AuthenticationProvider
, select user data from your DB and authenticate:
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserRepository userRepository;
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
final UsernamePasswordAuthenticationToken upAuth = (UsernamePasswordAuthenticationToken) authentication;
final String name = (String) authentication.getPrincipal();
final String password = (String) upAuth.getCredentials();
final String storedPassword = userRepository.findByName(name).map(User::getPassword)
.orElseThrow(() -> new BadCredentialsException("illegal id or passowrd"));
if (Objects.equals(password, "") || !Objects.equals(password, storedPassword)) {
throw new BadCredentialsException("illegal id or passowrd");
}
final Object principal = authentication.getPrincipal();
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
Collections.emptyList());
result.setDetails(authentication.getDetails());
return result;
}
...
- Configure with
WebSecurityConfigurerAdapter
for using aboveAuthenticationProvider
:
@EnableWebSecurity
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider authProvider;
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
http.authenticationProvider(authProvider);
}
}
refs:
Solution 3
First of all, create a form for registering your user.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!doctype html>
<html lang="en">
<head>
<title>Register New User Form</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Reference Bootstrap files -->
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div>
<div id="loginbox" style="margin-top: 50px;"
class="mainbox col-md-3 col-md-offset-2 col-sm-6 col-sm-offset-2">
<div class="panel panel-primary">
<div class="panel-heading">
<div class="panel-title">Register New User</div>
</div>
<div style="padding-top: 30px" class="panel-body">
<!-- Registration Form -->
<form:form action="${pageContext.request.contextPath}/register/processRegistrationForm"
modelAttribute="user"
class="form-horizontal">
<!-- Place for messages: error, alert etc ... -->
<div class="form-group">
<div class="col-xs-15">
<div>
<!-- Check for registration error -->
<c:if test="${registrationError != null}">
<div class="alert alert-danger col-xs-offset-1 col-xs-10">
${registrationError}
</div>
</c:if>
</div>
</div>
</div>
<!-- User name -->
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<form:input path="username" placeholder="username" class="form-control" />
</div>
<!-- Password -->
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<form:password path="password" placeholder="password" class="form-control" />
</div>
<!-- Register Button -->
<div style="margin-top: 10px" class="form-group">
<div class="col-sm-6 controls">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</div>
</form:form>
</div>
</div>
</div>
</div>
</body>
</html>
And write an action method in the controller to get the form information and save user in the database.
@Controller
@RequestMapping( "/register" )
public class RegistrationController
{
@Autowired
private UserDetailsManager userDetailsManager;
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
private Logger logger = Logger.getLogger( getClass().getName() );
@InitBinder
public void initBinder( WebDataBinder dataBinder )
{
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor( true );
dataBinder.registerCustomEditor( String.class, stringTrimmerEditor );
}
@PostMapping( "/processRegistrationForm" )
public String processRegistrationForm( @Valid @ModelAttribute( "user" ) com.exmaple.spring_demo.entity.User user, BindingResult theBindingResult, Model theModel )
{
String userName = user.getUsername();
logger.info( "Processing registration form for: " + userName );
// form validation
if ( theBindingResult.hasErrors() )
{
theModel.addAttribute( "user", new com.exmaple.spring_demo.entity.User() );
theModel.addAttribute( "registrationError", "User name/password can not be empty." );
logger.warning( "User name/password can not be empty." );
return "security/user/registration-form";
}
// check the database if user already exists
boolean userExists = doesUserExist( userName );
if ( userExists )
{
theModel.addAttribute( "user", new com.exmaple.spring_demo.entity.User() );
theModel.addAttribute( "registrationError", "User name already exists." );
logger.warning( "User name already exists." );
return "security/user/registration-form";
}
//
// whew ... we passed all of the validation checks!
// let's get down to business!!!
//
// encrypt the password
String encodedPassword = passwordEncoder.encode( user.getPassword() );
// prepend the encoding algorithm id
encodedPassword = "{bcrypt}" + encodedPassword;
// give user default role of "ROLE_USER"
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList( "ROLE_USER" );
// create user object (from Spring Security framework)
User tempUser = new User( userName, encodedPassword, authorities );
// save user in the database
userDetailsManager.createUser( tempUser );
logger.info( "Successfully created user: " + userName );
return "security/user/registration-confirmation";
}
@GetMapping( "/showRegistrationForm" )
public String showMyLoginPage( Model theModel )
{
theModel.addAttribute( "user", new com.exmaple.spring_demo.entity.User() );
return "security/user/registration-form";
}
private boolean doesUserExist( String userName )
{
logger.info( "Checking if user exists: " + userName );
// check the database if the user already exists
boolean exists = userDetailsManager.userExists( userName );
logger.info( "User: " + userName + ", exists: " + exists );
return exists;
}
}
And now, Define DataSource in Spring Configuration.
package com.exmaple.spring_demo.config;
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
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 org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import com.mchange.v2.c3p0.ComboPooledDataSource;
@Configuration
@EnableWebSecurity
@ComponentScan( "com.exmaple.spring_demo.config" )
@PropertySource( "classpath:persistence-mysql.properties" )
public class SecurityConfigJDBC extends WebSecurityConfigurerAdapter
{
/**
* set up variable to hold the properties
*/
@Autowired
private Environment env;
// define a bean for our security datasource
@Bean
public DataSource dataSource()
{
// create connection pool
ComboPooledDataSource securityDataSource = new ComboPooledDataSource();
// set the jdbc driver class
try
{
securityDataSource.setDriverClass( env.getProperty( "jdbc.driver" ) );
}
catch ( PropertyVetoException exc )
{
throw new RuntimeException( exc );
}
// log the connection props
// for sanity's sake, log this info
// just to make sure we are REALLY reading data from properties file
System.out.println( ">>> jdbc.url=" + env.getProperty( "jdbc.url" ) );
System.out.println( ">>> jdbc.user=" + env.getProperty( "jdbc.user" ) );
// set database connection props
securityDataSource.setJdbcUrl( env.getProperty( "jdbc.url" ) );
securityDataSource.setUser( env.getProperty( "jdbc.user" ) );
securityDataSource.setPassword( env.getProperty( "jdbc.password" ) );
// set connection pool props
securityDataSource.setInitialPoolSize( getIntProperty( "connection.pool.initialPoolSize" ) );
securityDataSource.setMinPoolSize( getIntProperty( "connection.pool.minPoolSize" ) );
securityDataSource.setMaxPoolSize( getIntProperty( "connection.pool.maxPoolSize" ) );
securityDataSource.setMaxIdleTime( getIntProperty( "connection.pool.maxIdleTime" ) );
return securityDataSource;
}
@Bean
public UserDetailsManager userDetailsManager()
{
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource( dataSource() );
return jdbcUserDetailsManager;
}
@Override
protected void configure( AuthenticationManagerBuilder auth ) throws Exception
{
auth.jdbcAuthentication().dataSource( dataSource() );
}
@Override
protected void configure( HttpSecurity http ) throws Exception
{
http.authorizeRequests()
.antMatchers( "/home/**" ).hasRole( "USER" )
.antMatchers( "/manager/**" ).hasRole( "MANAGERS" )
.antMatchers( "/admin/**" ).hasRole( "ADMIN" )
.and()
.formLogin()
.loginPage( "/showMyLoginPage" )
.loginProcessingUrl( "/authenticateTheUser" )// submit form data
.permitAll()
.and()
.logout().permitAll()
.and()
.exceptionHandling().accessDeniedPage( "/access-denied" )
.and()
.csrf()
.disable();
}
/**
* need a helper method
* read environment property and convert to int
*/
private int getIntProperty( String propName )
{
String propVal = env.getProperty( propName );
// now convert to int
int intPropVal = Integer.parseInt( propVal );
return intPropVal;
}
}
And finally, Create JDBC Properties File in src/main/resources/persistence-mysql.properties.
#
# JDBC connection properties
#
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_security_demo?useSSL=false
jdbc.user=springstudent
jdbc.password=springstudent
#
# Connection pool properties
#
connection.pool.initialPoolSize=5
connection.pool.minPoolSize=5
connection.pool.maxPoolSize=20
connection.pool.maxIdleTime=3000
The standard JDBC implementation of the UserDetailsService (JdbcDaoImpl) requires tables to load the password, account status (enabled or disabled) and a list of authorities (roles) for the user. You will need to adjust this schema to match the database dialect you are using.
CREATE TABLE `authorities` (
`username` varchar(50) NOT NULL,
`authority` varchar(50) NOT NULL,
UNIQUE KEY `authorities_idx_1` (`username`,`authority`),
CONSTRAINT `authorities_ibfk_1`
FOREIGN KEY (`username`)
REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`username` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`enabled` tinyint(1) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Przemysław Malinowski
Updated on August 21, 2020Comments
-
Przemysław Malinowski almost 4 years
I save users in a DB table via Hibernate and I am using Spring Security to authenticate:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.*; import org.springframework.security.config.annotation.authentication.builders.*; import org.springframework.security.config.annotation.web.configuration.*; @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER"); } }
And this works perfectly, but there is a point - user is loaded during server start. I need to write method RegisterUser(User user) that add new user to Spring Security in runtime. This method should focus only on this task. I dont know how to start to implement this feature so thanks for any advices! ;)
Ofc User have fields like login, password, role string etc etc...
Please do not post solutions with Spring MVC. This system is RESTful app using Spring Web Boost and Spring Security Boost in version 4.0.x
-
holmis83 almost 9 yearsThere are Spring Security + Hibernate examples out there, give it a search. For example: mkyong.com/spring-security/…
-
AndroLife about 8 yearsplease did you find a solution for your problem could you post the code ?
-
-
Philipp Jahoda about 8 yearsWhere do I create this user and SecurityContext?
-
Neil McGuigan about 8 years@PhilippJahoda SecurityContext is created by Spring. You would want to create the user in your registration logic, after getting info from the user (such as their desired username and password), probably in a service layer used by a controller
-
Philipp Jahoda about 8 yearsThank you for your response. I am using the spring provided /oauth/token URL, is that not correct? Where can I access the username and password provided to this url?
-
Cardinal System about 3 yearsI have been trying to figure this out. Where is
UserRepository
constructed? -
DEWA Kazuyuki - 出羽和之 about 3 yearsUserRepository is constructed automatically by Spring Boot. Core concepts section and Spring Data JPA Repositories section may help you.