【问题标题】:Selection based AuthProvider Spring Security基于选择的 AuthProvider Spring Security
【发布时间】:2018-11-13 07:10:33
【问题描述】:

我已将 Spring 安全性配置为与基于 LDAP 和 DB 的登录一起使用。首先,它尝试通过 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 用户都应该能够访问。

【问题讨论】:

  • 您的Filter 实现如何?我想知道您是否正在检查要在filter 中执行哪种身份验证,如果身份验证失败该怎么办?你看到任何错误吗?
  • 从 xml 中可以看出,有 2 个过滤器。第一个是提取 LDAP 检查的标头值。第二个是使用过滤器传递第三个值,即验证码。之后在这两种情况下 getAuthenticationManager().authenticate(authentication);叫做。过滤器不会执行额外的操作,而是将控制权传递给身份验证管理器
  • 这将允许用户无需身份验证即可进入该页面。
  • 您想根据用户选择更改“spring security config”!?我认为您将无法“动态更改配置”,但似乎是一种方法:1.“新屏幕”的“permitAll” 2.设置(会话)标志(由您指定) 3.检查标记并相应调整您的过滤器..另一种方法是:multiple security configurations
  • 编辑答案以根据输入选择身份验证提供程序并根据条件重定向到不同的登录页面,如果这是您的原始问题

标签: java spring spring-security


【解决方案1】:

如果您查看UsernamePasswordAuthenticationFilter 中的代码,则有setDetails 方法。

来自docs

提供以便子类可以配置放入 身份验证请求的 details 属性。

从这里开始的想法 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,我们需要扩展这个类。

您必须扩展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;
    }
}

CustomWebAuthenticationDetailsS​​ource.java

public class CustomWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {

    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new CustomAuthenticationDetails(context);
    }

}

请注意这些课程仅用于演示目的。

需要autowire这些类中的实际身份验证提供程序。

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

【讨论】:

  • 如果在创建 WebAuthenticationDetails 时获得“未找到线程绑定请求”怎么办?将其标记为@Component 以便在创建自定义过滤器时找到 bean
  • @user2501323 这是一个问题还是澄清?
  • @Haider 没有。如果你理解这个概念,我认为你应该能够实现它。
猜你喜欢
  • 2010-12-30
  • 2021-02-15
  • 2015-01-15
  • 2019-06-27
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 2017-10-19
  • 2012-02-09
相关资源
最近更新 更多