Spring security:在运行时注册用户

Posted

技术标签:

【中文标题】Spring security:在运行时注册用户【英文标题】:Spring security: register users during runtime 【发布时间】:2018-04-10 15:29:39 【问题描述】:

我有一个有两个端点的服务:

    公共端点:任何人都可以访问它,并开设一个用户帐户(注册) 受保护的端点:只有注册用户才能访问它,通过将授权标头用作 HTTP POST 请求的一部分

用例:

用户首先点击公共端点并通过HTTP POST 包含userName 的JSON 打开帐户。然后,该服务会生成密码,并将其作为 JSON 响应传回给用户。 用户从服务取回密码后,他应该使用这个password(连同他的userName)通过在受保护的端点 >授权标头

现在,显然需要在运行时注册新用户。

我面临的问题是,当第一个用户在公共端点上注册时,在那之后就不再需要进行身份验证来访问受保护的端点!每个凭据都可以使用,甚至没有 Authorized Header 的请求也可以使用。我不知道为什么我会出现这种不良行为,所以任何关于如何解决它的建议都会很棒!

用于打开用户帐户的公共端点

@RequestMapping(method = RequestMethod.POST, value = "/user", produces = "application/json")
public ResponseEntity<UserCreatedResponse> create(@RequestBody String userName) 
        // generate user password
        String password = service.generatePassword();

        // save the user to the local repository
        service.save(userName, password);

        // use SecurityService to add a new user token --> something fishy here!
        security.login(userName, password);


        // returns response with a new user password
        return new ResponseEntity<UserCreatedResponse>(
                new UserCreatedResponse(password),
                HttpStatus.CREATED);

UserService.java 用于将用户保存到存储库

public void save(String userName, String password) 
    repository.save(new User(userName, passwordEncoder.encode(password)));

SecurityService.java 用于存储登录凭据:我不确定这是否正确

public void login(String userName, String password) 
        // usrDetailsService is the instance of UserDetailsService.java
        UserDetails usrDetails = usrDetailsService.loadUserByUsername(userName);

        UsernamePasswordAuthenticationToken token = 
                new UsernamePasswordAuthenticationToken(usrDetails, password, usrDetails.getAuthorities());

        // authenticate token with the given account details
        authManager.authenticate(token);

        if (token.isAuthenticated()) 
            // provide authentication info to the context
            SecurityContextHolder.getContext().setAuthentication(token);
        
    

UserDetailsS​​ervice.java

public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException 
    // this is my custom User class
    User user = repository.findById(userName);

    // and this is org.springframework.security.core.userdetails.User
    return new User(user.getUsername(), user.getPasswordHash(), Collections.emptySet());

受保护(授权)端点

@RequestMapping(method = RequestMethod.POST, value = "/hello", produces = "application/json")
public String hello(@RequestBody MyRequest request) 
    return "Hello, authenticated!";

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.authorizeRequests()
                            .antMatchers(HttpMethod.POST, "/user") 
                            .permitAll().anyRequest().authenticated();

        http.csrf().disable();
    

同样,想要的行为是:

在“localhost:8080/user”上创建新用户帐户 然后点击“localhost:8080/hello”,这需要对上面注册的任何用户进行身份验证,否则以 Unathorized 响应

缺少什么或我做错了什么?

【问题讨论】:

有人可以帮忙吗?就像它在“/user”控制器被击中注册新用户时登录用户,然后在我调用经过身份验证的控制器“/hello”之后,它不再要求身份验证,而它应该!如何解决这个问题? 【参考方案1】:

您的安全配置是什么样的? 您可以指定应打开哪些页面进行注册:

  /**
   * Configures the access rights for the application.
   *
   * @param http the HttpSecurity context of the application
   * @throws Exception some exception.
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception 
    http.authorizeRequests()
        //Which URLs are available without any authentication
        .antMatchers("/about", "/help", "/imprint", "/register", "/registerExp", "/login**").permitAll()
        //Restrict access rights for any relative URL behind /admin/ to the ADMIN role
        .antMatchers("/admin/**").hasRole("ADMIN")
        //Any request needs to be fully authenticated (remember me does NOT suffice!)
        .anyRequest().fullyAuthenticated().and()
        //Specify the login process.
        .formLogin()
            .loginPage("/login")
            .failureHandler(authenticationHandler)
            .permitAll()
            .and().logout().permitAll()
            .and()
        .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).ignoringAntMatchers("/console/**");

    //Disables header security. This allows the use of the h2 console.
    http.headers().frameOptions().disable();
  

【讨论】:

添加 SecurityConfig 类 基本上我有所有需要的东西作为你的安全配置,我只是没有“formLogin()”部分,因为我没有任何登录表单,它是一个纯粹的服务和身份验证应该基于Authentication Header。我不明白为什么在“/user”端点注册后点击“/hello”控制器时它不询问登录详细信息。注册前“/hello”需要授权,注册后不需要。虽然它应该......有什么想法吗? 这是因为您在注册后授权新用户。你调用 `security.login(userName, password);` 使用SecurityContextHolder.getContext().setAuthentication(token); 设置身份验证。因此,您的用户无需再次登录即可访问所有受保护的网站 好吧,如果我删除该部分,那么它会要求进行身份验证,但凭据永远不会被识别。 嗯不确定这是否有帮助,但我使用 userDetailsManager 创建一个新用户:org.springframework.security.core.userdetails.User newSpringUser = new org.springframework.security.core.userdetails.User( user.getEmail(), encoder.encode(user.getPassword()), userAuthorities); userDetailsManager.createUser(newSpringUser); 当我需要我的应用程序用户(不是 spring 用户)时,我只是比较用户名或电子邮件或其他从我的应用程序用户的安全上下文中

以上是关于Spring security:在运行时注册用户的主要内容,如果未能解决你的问题,请参考以下文章

Grails / Spring Security - 注册电子邮件未解锁帐户

grails spring security自定义用户注册页面

:REST spring security - 手动验证新用户并获取访问令牌

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

Spring Security 用户账号注册、创建和管理

Grails - Spring Security 帐户创建