如何使用 Spring Security 做一个 RESTful 登录 API?

Posted

技术标签:

【中文标题】如何使用 Spring Security 做一个 RESTful 登录 API?【英文标题】:How to do a RESTful login API using Spring Security? 【发布时间】:2020-12-21 00:03:45 【问题描述】:

我刚刚开始使用 Spring 安全性,并且刚刚实现了可以正常工作的 HTTP 基本身份验证。

现在我想创建登录端点并尝试获取经过身份验证的用户。

我不确定我必须为此使用什么?我确实在网上阅读了很多东西,但不知道从哪里开始。

任何提示将不胜感激!

这是用于身份验证的代码。

SecurityConfiguration.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Autowired
    private UserDetailsService userDetailsService;

   @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(encodePWD());
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http
            .csrf().disable();
        http
            .httpBasic()
                .and()
            .authorizeRequests()
                .antMatchers("/rest/**").permitAll()
                .and()
            .authorizeRequests()
                .antMatchers("/secure/**").hasAnyRole("ADMIN")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .permitAll();
    

    @Bean
    public BCryptPasswordEncoder encodePWD() 
        return new BCryptPasswordEncoder();
    

CustomUserDetailsS​​ervice.java

@Service
public class CustomUserDetailsService implements UserDetailsService 

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        User user = userRepository.findByUsername(username);
        CustomUserDetails userDetails = null;

        if(user != null)
            userDetails = new CustomUserDetails();
            userDetails.setUser(user);
        else
            throw new UserNotFoundException("User doesn't exist with this username: " + username);
        
        return userDetails;
    

CustomUserDetails.java

@Getter
@Setter
public class CustomUserDetails implements UserDetails 

    private User user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() 
        return user.getRoles().stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole())).collect(Collectors.toSet());

    

    @Override
    public String getPassword() 
        return user.getPassword();
    

    @Override
    public String getUsername() 
        return user.getUsername();
    

    @Override
    public boolean isAccountNonExpired() 
        return true;
    

    @Override
    public boolean isAccountNonLocked() 
        return true;
    

    @Override
    public boolean isCredentialsNonExpired() 
        return true;
    

    @Override
    public boolean isEnabled() 
        return true;
    

更新

我正在使用 Postman 发送我的数据(如 JSON)


    "username": "admin",
    "password": "admin35"      

【问题讨论】:

【参考方案1】:

在传统的 MVC Spring Boot 应用程序中,Spring Security 会检查 SecurityContextHolder 以获取身份验证信息。如果没有找到,那么您将被重定向到登录页面。

对于 REST API 端点,您不会收到从服务器发送的典型登录表单。您将从客户端(AngularJS、ReactJS、VueJs、ios 应用程序等)上的表单收集凭据并将凭据发布到端点以检索用户信息。比如下面对用户进行认证,然后将认证后的用户信息返回给客户端。

private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @GetMapping("/user")
    public ResponseEntity<UserResponse> getUser(@AuthenticationPrincipal DmsUserDetails dmsUser) 
        
        logger.info("User " + dmsUser.getFirstName() + " " + dmsUser.getLastName() + " logged in.");
        
        UserResponse user = new UserResponse();
        user.setUserId(dmsUser.getUserId());
        user.setFirstName(dmsUser.getFirstName());
        user.setLastName(dmsUser.getLastName());
        user.setEmail(dmsUser.getUsername());
        
        return ResponseEntity.ok(user);
    

客户端会跟踪用户是否登录,并在每个后续请求中传递 cookie 或令牌。

在本例中,我将端点命名为 /user,但如果您愿意,也可以将其命名为 /login

【讨论】:

嗨,我确实调试了它,我的 loadUserByUsername 方法没有接收到用户名。当我使用 spring 安全表单登录但当我发送数据作为 JSON 用户名为空时,它会收到它。这是我用于身份验证的所有代码。 您如何通过 Postman 发送用户名/密码? Http 基本身份验证需要发送 base64 用户名:密码。如果您使用预定义的基本身份验证授权类型,邮递员将执行此操作。 发送原始 json 请求正文不适用于基本身份验证。您需要单击邮递员中的身份验证选项卡,从下拉框中选择基本身份验证,然后在此处输入您的用户名和密码字段。然后 Postman 将对字段进行 base64 编码并将其发送到身份验证标头中,这正是 Spring Security 所期望的。 是的,当我在邮递员中使用身份验证选项卡时,它可以正常工作。但是,如果我想使用像 /login 这样的 rest api 并发送 JSON,那么使用 Basic Auth 是不可能的?因为在这台服务器上,我现在将连接 android 应用程序,并且有一个登录自定义表单......我可以使用一些过滤器,如 UsernamePasswordAuthenticationFilter 还是不可能做到这一点?我是否必须使用 OAuth2 之类的东西或与令牌一起使用的东西?

以上是关于如何使用 Spring Security 做一个 RESTful 登录 API?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 和 Shiro 该如何选择?

如何使用 Spring Security + Angular 登录/验证

如何使用 DaoAuthenticationProvider 以编程方式使用 Spring Security 对用户进行身份验证

Spring Security 是如何在 Servlet 应用中执行的?

手把手教你如何使用Spring Security(下):访问控制

如何在 Javascript 中使用 Spring Security 用户名