我已将 Spring 安全性配置为使用 LDAP 和基于数据库的登录。首先,它会尝试通过 LDAP 登录,如果所需权限不存在,则会进入用户名/密码输入页面。
<security:http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<security:custom-filter ref="customPreAuthFilter" position="PRE_AUTH_FILTER"/> // This is for LDAP
<security:custom-filter ref="customAuthFilter" position="FORM_LOGIN_FILTER"/> // This is for DB Based
/** intercept urls
**/
</security:http>
我想在顶部添加一个新屏幕,用户需要在 LDAP 或用户名/密码这两个按钮之间进行选择。我该如何继续?
要访问的数据是相同的 url,即/home,但 ldap 和 DB 用户都应该能够访问。
最佳答案
如果您查看 UsernamePasswordAuthenticationFilter
中的代码,可以看到 setDetails
方法。
来自 docs :
Provided so that subclasses may configure what is put into the authentication request's details property.
创意来自这里 Provision to change ldap/Ad provider url at run time
您可以在此处设置诸如 authtype 之类的详细信息并使用它作为身份验证提供程序,但是要实现您想要的东西,需要做更多的工作。
补充细节,希望对您有所帮助。
CustomUsernamePasswordAuthenticationFilter.java
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
@Component
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(CustomUsernamePasswordAuthenticationFilter.class);
@Autowired
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
// TODO Auto-generated method stub
super.setAuthenticationManager(authenticationManager);
}
@Autowired
@Override
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
super.setAuthenticationDetailsSource(authenticationDetailsSource);
}
@Override
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
String authType = request.getParameter("authType");
logger.info("authType {} ",authType);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}
但这还不够,您需要扩展 WebAuthenticationDetails
。
原因是 WebAuthenticationDetails
只提供了 remote IP address
和 sessionId
所以,要添加 authType 我们需要扩展这个类。
您必须扩展 WebAuthenticationDetailsSource
以返回 CustomAuthenticationDetails
,如下所示。
CustomAuthenticationDetails.java
public class CustomAuthenticationDetails extends WebAuthenticationDetails{
private final String authType;
public CustomAuthenticationDetails(HttpServletRequest request) {
super(request);
authType = request.getParameter("authType");
}
public String getAuthType() {
return authType;
}
}
CustomWebAuthenticationDetailsSource.java
public class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomAuthenticationDetails(context);
}
}
请注意这些类仅用于演示目的。
需要 Autowiring
这些类中的实际身份验证提供程序。
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.stereotype.Component;
@Component
public class AuthenicationProviderJdbcLdapImpl implements AuthenticationProvider{
// you need to autowire jdbc auth provider
@Autowired(required = false)
DaoAuthenticationProvider authenticationProvider;
//you need to autowire ldap auth provider
@Autowired(required = false)
LdapAuthenticationProvider ldapAuthenticationProvider;
protected static class User{
private final String userId;
private final String password;
public User(String userId,String password) {
this.userId = userId;
this.password = password;
}
public String getUserId() {
return userId;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "User [userId=" + userId + ", password=" + password + "]";
}
}
private final static Logger logger = LoggerFactory.getLogger(AuthenicationProviderJdbcLdapImpl.class);
private static final List<User> users1 = Arrays.asList(new User("admin1", "password"),new User("admin2", "password"));
private static final List<User> users2 = Arrays.asList(new User("admin3", "password"),new User("admin4", "password"));
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
CustomAuthenticationDetails details = (CustomAuthenticationDetails) authentication.getDetails();
String authType = details.getAuthType();
logger.info("authType {}",authType);
if("jdbc".equalsIgnoreCase(authType)) {
logger.info("perfrom jdbc authentication");
//perform your authentication using jdbc
//the below is just for explaination
return performAuthentication(authentication, users1);
}else if("ldap".equalsIgnoreCase(authType)) {
logger.info("perfrom ldap authentication");
//perform your authentication using ldap
//the below is just for explaination
return performAuthentication(authentication, users2);
}
return null;
}
private Authentication performAuthentication(Authentication authentication,List<User> users) {
String credential = (String) authentication.getCredentials();
String userId = authentication.getName();
for(User user: users) {
if(user.getUserId().equals(userId)&& user.getPassword().equals(credential)) {
authentication = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(),authentication.getAuthorities());
return authentication;
}
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
如果您需要重定向不同的登录页面(不确定,如果您有要求),您可以注册安全配置中显示的 AuthenticationFailureHandler
。这里根据条件重定向到login和login1。
http.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String whichPage = request.getParameter("whichPage");
System.out.println("inside login failure handler "+whichPage);
if("login1".equals(whichPage)) {
response.sendRedirect(request.getContextPath() +"/login1");
}else {
response.sendRedirect(request.getContextPath() +"/login");
}
}
})
WebSecurityConfig.java
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired()
AuthenicationProviderJdbcImpl authenicationProviderJdbcImpl;
@Autowired()
AuthenicationProviderLdapImpl authenicationProviderLdapImpl;
@Autowired
CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(customUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration","/login1").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()//.successHandler(authenticationSuccessHandler)
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String whichPage = request.getParameter("whichPage");
System.out.println("inside login failure handler "+whichPage);
if("login1".equals(whichPage)) {
response.sendRedirect(request.getContextPath() +"/login1");
}else {
response.sendRedirect(request.getContextPath() +"/login");
}
}
})
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenicationProviderLdapImpl).authenticationProvider(authenicationProviderJdbcImpl);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());*/
}
}
以下是authType = jdbc
或authType=ldap
时的日志
login called
2018-11-23 17:45:25.606 INFO 7232 --- [nio-8080-exec-6] stomUsernamePasswordAuthenticationFilter : authType jdbc
2018-11-23 17:45:25.606 INFO 7232 --- [nio-8080-exec-6] c.t.a.AuthenicationProviderJdbcLdapImpl : authType jdbc
2018-11-23 17:45:25.606 INFO 7232 --- [nio-8080-exec-6] c.t.a.AuthenicationProviderJdbcLdapImpl : perfrom jdbc authentication
login called
login1 called
login1 called
2018-11-23 17:45:42.435 INFO 7232 --- [nio-8080-exec-5] stomUsernamePasswordAuthenticationFilter : authType ldap
2018-11-23 17:45:42.435 INFO 7232 --- [nio-8080-exec-5] c.t.a.AuthenicationProviderJdbcLdapImpl : authType ldap
2018-11-23 17:45:42.435 INFO 7232 --- [nio-8080-exec-5] c.t.a.AuthenicationProviderJdbcLdapImpl : perfrom ldap authentication returning true in ldap
关于java - 基于选择的 AuthProvider Spring Security,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53275645/