How to add new user to Spring Security at runtime

后端 未结 4 1428
难免孤独
难免孤独 2020-12-13 15:16

I save users in a DB table via Hibernate and I am using Spring Security to authenticate:

import org.springframework.beans.factory.annotation.Autowired;

impo         


        
相关标签:
4条回答
  • 2020-12-13 15:21

    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:

    1. 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;
        }
        ...
    
    1. Configure with WebSecurityConfigurerAdapter for using above AuthenticationProvider:
    @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:

    • Spring Security Architecture
    • complete code sample
    0 讨论(0)
  • 2020-12-13 15:21

    use this code to add authority to current user:

    List<GrantedAuthority> authorities = new ArrayList<>();
    authorities.add(new SimpleGrantedAuthority("ROLE_NEWUSERROLE'); 
        SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken(
                SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
                SecurityContextHolder.getContext().getAuthentication().getCredentials(),
                authorities)
            );
    
    0 讨论(0)
  • 2020-12-13 15:26

    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;
    

    0 讨论(0)
  • 2020-12-13 15:43

    You probably want to store your users in a database and not in memory, if they are registering :)

    1. Create the authorities for the user

      List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
      authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
      
    2. Instantiate the user (with a class implementing UserDetails)

      UserDetails user = new User("user@example.com", passwordEncoder.encode("s3cr3t"), authorities);
      
    3. Save the user somewhere useful. The JdbcUserDetailsManager can save a user to a database easily.

      userDetailsManager.createUser(user);
      
    4. Create a UsernamePasswordAuthenticationToken

      Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
      
    5. Add the Authentication to the SecurityContext

      SecurityContextHolder.getContext().setAuthentication(authentication);
      
    0 讨论(0)
提交回复
热议问题