使用 java 配置进行 n 因素身份验证 [已关闭]

在使用 spring 安全性的 spring mvc 应用中,我想使用自定义来检查默认值和 .我正在尝试使用Java配置。我应该如何设置它?AuthenticationProviderusernamepassword


答案 1

首先,关于您正在使用的接口以及它们在身份验证过程中所扮演的角色的一些解释:

  • 身份验证 - 表示对用户进行身份验证的结果。保存授予该用户的权限以及可能需要的有关该用户的任何其他详细信息。由于框架无法知道需要哪些详细信息,因此身份验证对象具有可以返回任何对象的getDetails方法

  • 身份验证提供程序 - 可以以某种方式创建对象的对象。为了使它们更易于重用,一些(或大多数)避免在对象上设置用户详细信息,因为每个应用程序可能需要特定的用户详细信息。相反,它们将解析用户详细信息的过程委托给可设置的AuthenticationAuthenticationProviderAuthenticationUserDetailsService

  • 用户详细信息服务 - 一种用于检索应用程序中所需的用户详细信息的策略。

因此,如果要创建自定义,则甚至可能不需要以需要.决定取决于你,取决于你是否计划在其他项目中重用你的实现。AuthenticationProviderUserDetailsService

至于代码中的编译问题,您将提供 .在 中,您已使用注释对字段进行了批注。这意味着,容器(在你的例子中是Spring应用程序上下文)是找到一个合适的实现,并在运行时使用反射将其注入该字段。通过上下文设置此字段的过程称为依赖关系注入。在类中,您尝试通过类中不存在的方法设置字段来自己提供实现。UserDetailsServiceCustomAuthenticationProvideruserService@InjectSecurityConfigsetUserDetailsService

要解决此问题,您需要决定使用其中一种方式来提供 UserDetails 服务,并且:

  • 删除注释并创建方法,或者@InjectsetUserDetailsService
  • 在调用不存在的方法时删除该行,并将 的实现声明为 beanUserDetailsService

至于你应该选择哪种方式,如果你能找到一种方法让你的类在其他项目中可重用,那么依赖注入方式可能会更好。在这种情况下,您可以导入它(通过使用 annotaion),并在下一个应用程序中将不同的实现声明为 Bean,并使其正常工作。SecurityConfig@ImportUserDetailsSerice

通常,像 这样的类并不是真正可重用的,因此创建 setter 并删除依赖注入可能是我的首选。SecurityConfig

编辑

一个工作,尽管是一个简单的实现(主要基于这个博客条目)将是:

public class CustomAuthenticationProvider implements AuthenticationProvider{

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        List<GrantedAuthority> grantedAuths = new ArrayList<>();
        if (name.equals("admin") && password.equals("system")) {
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));  
        } 
        if(pincodeEntered(name)){
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_PINCODE_USER"));  
        }
        Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

    private boolean pincodeEntered(String userName){
        // do your check here
        return true;
    }
}

然后在配置类中更改以下方法:

@Bean
AuthenticationProvider customAuthenticationProvider() {
        return new CustomAuthenticationProvider();
}

答案 2

我们需要做的第一件事是扩展UsernamePasswordAuthenticationFilter类,以便它可以处理第二个输入字段。

public class TwoFactorAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
    private String extraParameter = "extra";
    private String delimiter = ":";
    //getters and setters

    @Override
    protected String obtainUsername(HttpServletRequest request)
    {
        String username = request.getParameter(getUsernameParameter());
        String extraInput = request.getParameter(getExtraParameter());
        String combinedUsername = username + getDelimiter() + extraInput;
        return combinedUsername;
    }

}

获取用户名()此方法是从传入的 HttpServletRequest 对象中检索用户名和“额外”输入字段。

然后,它将这两个值连接成一个字符串,用分隔符字符串(默认情况下为冒号)分隔它们。

然后,它返回此组合字符串。默认情况下,从中读取“额外”输入字段的参数是额外的。

用户详细信息服务应如下所示:

@Override
public UserDetails loadUserByUsername(String input) throws UsernameNotFoundException, DataAccessException
{
    String[] split = input.split(":");
    if(split.length < 2)
    {
        throw new UsernameNotFoundException("Must specify both username and corporate domain");
    }

    String username = split[0];
    String domain = split[1];
    User user = userDao.findByUsernameAndDomain(username, domain);
    if(user == null)
    {
        throw new UsernameNotFoundException("Invalid username or corporate domain");
    }
    return user;
}

将给定的用户名拆分为两个部分:用户名和额外字段。在此示例中,额外的字段是用户的企业域。

一旦我们有了用户名和域,我们就可以使用我们的DAO来查找匹配的用户。

最后一个谜题:

TwoFactorAuthenticationFilter:

    <http use-expressions="true" auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
        <intercept-url pattern="/secured" access="isAuthenticated()" />
        <intercept-url pattern="/**" access="permitAll" />
        <custom-filter position="FORM_LOGIN_FILTER" ref="twoFactorAuthenticationFilter" />
        <logout logout-url="/logout" />
    </http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="authenticationProvider" />
    </authentication-manager>

    <beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="passwordEncoder">
            <beans:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
        </beans:property>
        <beans:property name="userDetailsService" ref="userService" />
    </beans:bean>

    <beans:bean id="userService" class="com.awnry.springexample.UserDetailsServiceImpl" />

    <beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:property name="loginFormUrl" value="/login" />
    </beans:bean>

    <beans:bean id="twoFactorAuthenticationFilter" class="com.awnry.springexample.TwoFactorAuthenticationFilter">
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="authenticationFailureHandler" ref="failureHandler" />
        <beans:property name="authenticationSuccessHandler" ref="successHandler" />
        <beans:property name="filterProcessesUrl" value="/processLogin" />
        <beans:property name="postOnly" value="true" />
        <beans:property name="extraParameter" value="domain" />
    </beans:bean>

    <beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <beans:property name="defaultTargetUrl" value="/login" />
    </beans:bean>

    <beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <beans:property name="defaultFailureUrl" value="/login?login_error=true" />
    </beans:bean>

在 twoFactorAuthenticationFilter bean 定义中,我们将 extraParameter 属性设置为“domain”,这是要在登录表单中使用的输入字段的名称。

编辑:

查看 User 类的构造函数。

如果您不知道被授予的权限进入什么,请查看以下链接:

http://docs.spring.io/autorepo/docs/spring-security/3.2.1.RELEASE/apidocs/org/springframework/security/core/GrantedAuthority.html

您的编码给出了仅适用于普通用户名和密码的不同模式。我的代码适用于 n 因素身份验证。尝试切换到我的代码,如果任何问题仍然存在。


推荐