Spring 如何在authenticationManagerBuilder上注册多个UserDetails服务
我有两个不同的存储库供普通用户和管理员使用,并有单独的url端点进行身份验证。我希望身份验证管理器对端点使用单独的Spring 如何在authenticationManagerBuilder上注册多个UserDetails服务,spring,spring-boot,authentication,spring-security,userdetailsservice,Spring,Spring Boot,Authentication,Spring Security,Userdetailsservice,我有两个不同的存储库供普通用户和管理员使用,并有单独的url端点进行身份验证。我希望身份验证管理器对端点使用单独的UserDetailsService,因为普通用户和管理员用户可以有相同的用户名和密码,但存储库不同 例如: 如果端点命中为用户登录,则为用户详细信息服务1和 如果端点命中是admin_login,则为UserDetailsService2 如何实现这一点?HttpSecurity.formLogin DSL只支持单个登录URL,因为这是最常见的。但是,您可以通过显式注册第二个用户名
UserDetailsService
,因为普通用户和管理员用户可以有相同的用户名和密码,但存储库不同
例如:
如果端点命中为用户登录,则为用户详细信息服务1和
如果端点命中是admin_login,则为UserDetailsService2
如何实现这一点?HttpSecurity.formLogin DSL只支持单个登录URL,因为这是最常见的。但是,您可以通过显式注册第二个
用户名密码AuthenticationFilter
来做到这一点。有一些很好的图表说明了基于表单的登录是如何工作的
我创建了一个(确保使用链接的分支)。以下是正在发生的事情的摘要和描述:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// allow anyone to access the admin log in page
.mvcMatchers("/admin_login").permitAll()
// require admin access to any admin URLs
.mvcMatchers("/admin/**").hasRole("ADMIN")
// any other URL just requires to be authenticated
.anyRequest().authenticated()
.and()
// configure the user based authentication
.formLogin()
// this means you should serve a log in page for users at GET /user_login
// Spring Security will process POST /user_login as a user log in
.loginPage("/user_login")
// allow anyone to access the /user_login since they aren't authenticated when they see a log in page
.permitAll()
.and()
// formLogin above only supports a single repository because that is what is most common
// fortunately formLogin is just a shortcut for the code below
// here we add the admin login form explicitly
.addFilter(adminAuthenticationFilter());
}
// formLogin for users will pick up a UserDetailsService exposed as a Bean
@Bean
static InMemoryUserDetailsManager userDetailsManager() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
// create an admin version of UsernamePasswordAuthenticationFilter
private static UsernamePasswordAuthenticationFilter adminAuthenticationFilter() {
// inject the adminAuthenticationProvider so only admins are authenticated with this Filter
UsernamePasswordAuthenticationFilter result = new UsernamePasswordAuthenticationFilter(adminAuthenticationProvider());
// only process POST /admin_login
result.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin_login", "POST"));
// errors should go to /admin_login?error
result.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/admin_login?error"));
return result;
}
// create an AuthenticationManager that is only used by Admin users
private static AuthenticationManager adminAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(adminUsers());
return new ProviderManager(authenticationProvider);
}
// we use the same username as user based to demon that it will work properly
// the difference is that the password is admin and the user will have admin role so it can access URLs in /admin/
static InMemoryUserDetailsManager adminUsers() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("admin")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
}
如上所述,您负责创建登录页面并确保它们发布到正确的URL。第一步是创建一个控制器,将URL映射到要显示的视图。为了方便起见,这里我们使用单个控制器,但您可以将其拆分:
@Controller
public class LoginController {
@GetMapping("/admin_login")
String adminLogin() {
return "admin_login";
}
@GetMapping("/user_login")
String login() {
return "user_login";
}
}
然后需要有两个视图。第一个视图是admin_login.html。在Boot+Thymeleaf应用程序中,类似的内容将位于src/main/resources/templates/user\u login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Log In</title>
</head>
<body>
<div class="container">
<h1>User Log In</h1>
<form method="post"
th:action="@{/user_login}">
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<div>
<label for="username">Username</label>
<input type="text"
id="username"
name="username"
placeholder="Username"
required autofocus>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password"
id="password"
name="password"
placeholder="Password"
required>
</div>
<button type="submit">Sign in</button>
</form>
</div>
</body>
</html>
用户登录
用户登录
无效的用户名和密码。
您已注销。
用户名
密码
登录
我在上面提供的链接中对此进行了详细介绍。关键是它使用HTTP参数用户名
和密码
向/user\u login
提交帖子
对于管理员登录,您需要一个类似的视图,该视图使用HTTP参数
用户名和密码发布到/admin\u login
,您可以有这样的功能
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(service1)
.passwordEncoder(passwordEncoder());
authenticationManagerBuilder
.userDetailsService(service2)
.passwordEncoder(passwordEncoder());
}