如何在运行时向Spring Security添加新用户

时间:2015-08-27 08:50:58

标签: spring maven spring-security persistence registration

我通过Hibernate将用户保存在数据库表中,我使用Spring Security进行身份验证:

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");
    }
}

这很有效,但有一点 - 用户在服务器启动时加载。我需要编写方法RegisterUser(用户用户),在运行时将新用户添加到Spring Security。此方法应仅关注此任务。我不知道如何开始实现此功能,所以感谢任何建议! ;)

Ofc用户有登录,密码,角色等等字段......

请不要使用Spring MVC发布解决方案。该系统是使用版本4.0.x中的Spring Web Boost和Spring Security Boost的RESTful应用程序

4 个答案:

答案 0 :(得分:6)

您可能希望将用户存储在数据库中,而不是存储在内存中,如果他们正在注册:)

  1. 为用户

    创建authorities
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
    
  2. 实例化用户(使用实现UserDetails的类)

    UserDetails user = new User("user@example.com", passwordEncoder.encode("s3cr3t"), authorities);
    
  3. 将用户保存在有用的位置。 JdbcUserDetailsManager可以轻松地将用户保存到数据库。

    userDetailsManager.createUser(user);
    
  4. 创建UsernamePasswordAuthenticationToken

    Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authorities);
    
  5. 将身份验证添加到SecurityContext

    SecurityContextHolder.getContext().setAuthentication(authentication);
    

答案 1 :(得分:1)

您可以使用Spring Data JPA来创建用户。

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

用法:

User user = new User();
userRepository.save(user);

如何验证上方的用户:

  1. 创建自定义AuthenticationProvider,从数据库中选择用户数据并进行身份验证:
@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. 配置WebSecurityConfigurerAdapter以便在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);
    }
}

参考:

答案 2 :(得分:0)

使用此代码向当前用户添加权限:

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)
        );

答案 3 :(得分:0)

首先,创建一个用于注册用户的表单。

<%@ 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>

enter image description here

并在控制器中编写一个操作方法,以获取表单信息并将用户保存在数据库中。

@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;
    }
    
}

现在,在Spring Configuration中定义DataSource。

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;
    }


}

最后,在src / main / resources / persistence-mysql.properties中创建JDBC属性文件。

#
# 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

UserDetailsS​​ervice(JdbcDaoImpl)的标准JDBC实现要求表为用户加载密码,帐户状态(启用或禁用)和权限列表(角色)。您将需要调整此架构以匹配您正在使用的数据库方言。

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;

enter image description here