- Overview {#1-overview}
Spring Security 提供了不同的身份认证系统,例如通过数据库和 UserDetailService
。
有时可能不想使用 JPA 持久层,而是想使用 MongoDB Repository。本文将带你了解如何使用 Spring Security 和 MongoDB 对用户进行认证。
2、Spring Security 使用 MongoDB 进行认证 {#2spring-security-使用-mongodb-进行认证}
MongoDB Repository 与 JPA Repository 类似不过,我们需要设置不同的配置。
2.1、Maven 依赖 {#21maven-依赖}
本文使用嵌入式 MongoDB。首先,添加 spring-boot-starter-data-mongodb
和 de.flapdoodle.embed.mongo
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>3.3.1</version>
</dependency>
2.2、配置 {#22配置}
创建配置类:
@Configuration public class MongoConfig {
private static final String CONNECTION_STRING = "mongodb://%s:%d"; private static final String HOST = "localhost";
@Bean public MongoTemplate mongoTemplate() throws Exception {
int randomPort = SocketUtils.findAvailableTcpPort(); ImmutableMongodConfig mongoDbConfig = MongodConfig.builder() .version(Version.Main.PRODUCTION) .net(new Net(HOST, randomPort, Network.localhostIsIPv6())) .build(); MongodStarter starter = MongodStarter.getDefaultInstance(); MongodExecutable mongodExecutable = starter.prepare(mongoDbConfig); mongodExecutable.start(); return new MongoTemplate(MongoClients.create(String.format(CONNECTION_STRING, HOST, randomPort)), &quot;mongo_auth&quot;);
}
}
配置 AuthenticationManager
,例如,使用 HTTP Basic Authentication:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true) public class SecurityConfig {
//... public SecurityConfig(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; }
@Bean public AuthenticationManager customAuthenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject (AuthenticationManagerBuilder.class); authenticationManagerBuilder.userDetailsService(userDetailsService) .passwordEncoder(bCryptPasswordEncoder()); return authenticationManagerBuilder.build(); }
@Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); }
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() .and() .httpBasic() .and() .authorizeRequests() .anyRequest() .permitAll() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); return http.build(); }
}
2.3、User Domain 和 Repository {#23user-domain-和-repository}
首先,为身份认证(Authentication)定义一个简单的带角色的 User
,并且实现 UserDetails
接口,以继承 Principal
对象的常用方法:
@Document
public class User implements UserDetails {
private @MongoId ObjectId id;
private String username;
private String password;
private Set<UserRole> userRoles;
// get / set 方法
}
然后,定义一个 Repository:
public interface UserRepository extends MongoRepository<User, String> {
@Query("{username:'?0'}") User findUserByUsername(String username);
}
2.4、Authentication Service {#24authentication-service}
最后,实现 UserDetailService
,以检索用户并检查其是否通过身份认证:
@Service public class MongoAuthUserDetailService implements UserDetailsService { // ... @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
com.baeldung.mongoauth.domain.User user = userRepository.findUserByUsername(userName);
Set&lt;GrantedAuthority&gt; grantedAuthorities = new HashSet&lt;&gt;(); user.getAuthorities() .forEach(role -&gt; { grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole() .getName())); }); return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}
}
2.5、测试认证 {#25测试认证}
定义一个简单的 Controller 用于测试。
例如:定义两个不同的角色,以测试指定端点的身份认证和授权:
@RestController public class ResourceController {
@RolesAllowed("ROLE_ADMIN") @GetMapping("/admin") public String admin() { return "Hello Admin!"; }
@RolesAllowed({ "ROLE_ADMIN", "ROLE_USER" }) @GetMapping("/user") public String user() { return "Hello User!"; }
}
将所有内容封装在一个 Spring Boot 测试中,测试身份认证是否有效。
如下,对于提供无效凭证或在系统中不存在的用户,测试期望返回401状态码。
class MongoAuthApplicationTest {
// 设置
@Test void givenUserCredentials_whenInvokeUserAuthorizedEndPoint_thenReturn200() throws Exception { mvc.perform(get("/user").with(httpBasic(USER_NAME, PASSWORD))) .andExpect(status().isOk()); }
@Test void givenUserNotExists_whenInvokeEndPoint_thenReturn401() throws Exception { mvc.perform(get("/user").with(httpBasic("not_existing_user", "password"))) .andExpect(status().isUnauthorized()); }
@Test void givenUserExistsAndWrongPassword_whenInvokeEndPoint_thenReturn401() throws Exception { mvc.perform(get("/user").with(httpBasic(USER_NAME, "wrong_password"))) .andExpect(status().isUnauthorized()); }
@Test void givenUserCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn403() throws Exception { mvc.perform(get("/admin").with(httpBasic(USER_NAME, PASSWORD))) .andExpect(status().isForbidden()); }
@Test void givenAdminCredentials_whenInvokeAdminAuthorizedEndPoint_thenReturn200() throws Exception { mvc.perform(get("/admin").with(httpBasic(ADMIN_NAME, PASSWORD))) .andExpect(status().isOk());
mvc.perform(get(&quot;/user&quot;).with(httpBasic(ADMIN_NAME, PASSWORD))) .andExpect(status().isOk());
}
}
3、总结 {#3总结}
本文结介绍了 Spring Security 如何使用 MongoDB 进行用户的身份认证。
参考:https://www.baeldung.com/spring-security-authentication-mongodb