带有 Spring Security 的 Spring REST 不接受凭据

Posted

技术标签:

【中文标题】带有 Spring Security 的 Spring REST 不接受凭据【英文标题】:Spring REST with Spring Security not accepting credentials 【发布时间】:2019-01-05 02:56:35 【问题描述】:

我正在使用 Spring REST 创建一个 REST API,并尝试使用 Spring Security 对其进行保护以进行基本身份验证。它由 MariaDB 数据库提供支持。

我用过this guide for setting up basic authentication。我已经到了可以发出 POST 请求以在数据库中创建新用户凭据的位置,但是当我转身尝试使用这些凭据访问受保护的端点时,我得到了 401 Unauthorized 响应。

谁能指出我哪里出错了?谢谢!

这是我的实体:

@Entity
@Table(name="credentials")
public class Credential 

public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();

@Id
@GenericGenerator(name = "uuid", strategy = "uuid2")
@GeneratedValue(generator="uuid")
@Column(name="user_id")
private UUID userId;
@Column(name="username")
private String username;
@Column(name="password")
private String password;
@Column(name="updated_at")
private LocalDateTime updatedAt;
@Column(name="created_at")
private LocalDateTime createdAt;
@OneToOne(fetch=FetchType.EAGER)
@JoinColumn(name="user_id")
private User user;

public Credential(UUID userId, String username, String password, LocalDateTime updatedAt, LocalDateTime createdAt) 
    this.userId = userId;
    this.username = username;
    setPassword(password);
    this.updatedAt = updatedAt;
    this.createdAt = createdAt;


public Credential() 
...and standard getters and setters

这是我的存储库:

public interface CredentialRepository extends CrudRepository<Credential, UUID>

Optional<Credential> findByUsername(String username);


我的类扩展了 UserDetailsS​​ervice:

@Component
public class DetailsService implements UserDetailsService 

@Autowired
private CredentialRepository credentials;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
    Optional<Credential> credential = credentials.findByUsername(username);
    if (!credential.isPresent()) 
        throw new UsernameNotFoundException(username + " not found");
    
    return new User(credential.get().getUsername(), credential.get().getPassword(),
            AuthorityUtils.createAuthorityList("ROLE_USER"));


@Transactional
public Credential signupNewAccount(Credential credential) throws DuplicateUsernameException 
    if(usernameExists(credential.getUsername())) 
        throw new DuplicateUsernameException("That username is not available: " + credential.getUsername());
    
    Credential registered = new Credential();
    registered.setUsername(credential.getUsername());
    registered.setPassword(credential.getPassword());
    return credentials.save(registered);


private boolean usernameExists(String username) 
    Optional<Credential> candidate = credentials.findByUsername(username);
    if(candidate.isPresent()) 
        return true;
     else 
        return false;
    



还有我的 WebSecurityConfigurerAdapter:

@Component
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

@Autowired
private DetailsService detailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.userDetailsService(detailsService).passwordEncoder(Credential.PASSWORD_ENCODER);


@Override
protected void configure(HttpSecurity http) throws Exception 
    http
    .csrf().disable()
    .authorizeRequests()
    .antMatchers(HttpMethod.POST, "/v1/login").permitAll()
    .antMatchers(HttpMethod.POST, "/v1/signup").permitAll()
    .anyRequest().authenticated()
    .and().httpBasic()
    .and().sessionManagement().disable();


我发送到另一个端点的 HTTP GET 请求:

GET /v1/boards HTTP/1.1
Host: localhost:8443
Authorization: Basic dGVzdDp0ZXN0
Cache-Control: no-cache

以及来自服务器的响应:

HTTP/1.1 401
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=0FE1E2736168B8C24980059A2546CA95; Path=/; Secure; HttpOnly
WWW-Authenticate: Basic realm="Realm"
Content-Length: 0
Date: Sat, 28 Jul 2018 20:36:37 GMT

【问题讨论】:

【参考方案1】:

花了一天时间阅读了每篇关于 Spring Security 和 Basic Authentication 的文章后,我发现了问题所在。结果当我在数据库中注册凭据时,我对它们进行了两次加密。在我的 UserDetailsS​​ervice 实现中更改注册方法修复了它:

@Transactional
public Credential signupNewAccount(Credential credential) throws DuplicateUsernameException 
    System.out.println("Enter signupNewAccount: " + credential.getUsername() + " / " + credential.getPassword());
    if(usernameExists(credential.getUsername())) 
        throw new DuplicateUsernameException("That username is not available: " + credential.getUsername());
    
    return credentials.save(credential);

【讨论】:

以上是关于带有 Spring Security 的 Spring REST 不接受凭据的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud 值Spring-Security

Understand Spring Security Architecture and implement Spring Boot Security

Apache Shiro 与 Spring Security

Spring Security 不显示错误消息

Spring security oauth2 client_credentials认证

覆盖 spring-security BadCredentialsException 的容器响应