java - 基于选择的 AuthProvider Spring Security

标签 java spring spring-security

我已将 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 addresssessionId 所以,要添加 authType 我们需要扩展这个类。

您必须扩展 WebAuthenticationDetailsS​​ource 以返回 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;
    }
}

CustomWebAuthenticationDetailsS​​ource.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 = jdbcauthType=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/

相关文章:

java - 使用实体对象进行数据库管理

java - Spring MVC 表单提交绑定(bind)动态嵌套属性

asynchronous - spring security oauth2 与异步请求的使用

java - Spring Security 不会将 HTTP 重定向到 HTTPS

java - 获取 org.hibernate.MappingException : No Dialect mapping for JDBC type: -4 exception?

java - @Transactional 方法测试失败 :Concurrent update in table "WORK": another transaction has updated or deleted the same row

java - 如果 JUnit 测试因超时而失败,它会运行 finally 子句吗?

java - JDBC模板查询错误

java - Spring ,Maven : Unable to deploy module with tomcat

java - 配置 Spring Security 以使用自定义 UsernamePasswordAuthenticationFilter