如何通过多个参数来验证用户,而不仅仅是通过Spring中的名称来验证用户?

时间:2014-10-22 09:24:33

标签: java spring spring-security

我正在尝试使用Spring Security 3.2.5在登录时验证用户

这是一项简单的任务,我设法找到了很多如何做的例子。 我的问题是,在我的数据库中,用户是唯一的(用户名和组)。我需要为自定义UserDetailsS​​ervice提供这两个值才能检索用户。

在这种情况下,有人可以指导我找到最佳解决方案吗?

这是我到目前为止所提出的问题(如果我弄错了,我很困惑,请原谅我)

  • 通过创建自定义authenticationProvider并在authenticate方法中执行用户数据库检查。基本上,从MyUserDetailsS​​ervice移动我的loadByUserName实现来验证MyAuthenticationProvider中的方法。

    public class MyAuthenticationProvider implements AuthenticationProvider {
    
       @Override
       public Authentication authenticate(Authentication authentication) 
         throws AuthenticationException {
         String name = ...
         String password = ...
         String group = ...
         ....//I am not sure how to do it though
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
          return authentication.equals(MyUsernamePasswordGroupAuthenticationToken.class);
        }
    }
    
  • 可以将多个参数传递给loadUserByName。我想要像

    这样的东西
    loadUserByNameAndGroud(String name, String group)
    

    但我不知道如何使自定义authenticationProvider调用loadUserByNameAndGroud而不是loadUserByName

security.xml文件

<security:http use-expressions="true">
    <security:custom-filter ref="myAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
    <security:intercept-url pattern="/views/login*" access="isAnonymous()"/>
    <security:intercept-url pattern="/views/**" access="isAuthenticated()"/>
    <security:form-login login-page="/views/login.faces"
                         default-target-url="/"
                         username-parameter="username"
                         password-parameter="password"/>
    <security:logout logout-url="/logout" delete-cookies="JSESSIONID"/>
 </security:http>

<security:authentication-manager>
    <security:authentication-provider user-service-ref="myUserDetailsService" >
        <security:password-encoder ref="myPasswordEncoder"/>
    </security:authentication-provider>
</security:authentication-manager>

更新[自定义过滤器]

我尝试按照holmis83的建议实现自定义过滤器,但我得到了以下异常

BeanDefinitionParsingException: Configuration problem: Filter beans '<myAuthenticationFilter>' and '<org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#0>' have the same 'order' value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from <http> and avoiding the use of <http auto-config='true'>.

我添加到security.xml的自定义过滤器元素

<security:custom-filter ref="myAuthenticationFilter" position="FORM_LOGIN_FILTER"/>

我做错了什么?

3 个答案:

答案 0 :(得分:0)

尝试这样的事情

@Autowired
private UserDetailsService userDetailsService;


@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationServiceException {
    try {
        if (authentication instanceof MyUsernamePasswordGroupAuthenticationToken) {
            User user = userDetailsService.loadUserByNameAndGroup(((MyUsernamePasswordGroupAuthenticationToken) authentication).getName(), ((MyUsernamePasswordGroupAuthenticationToken) authentication).getGroup());
            if (user != null) {
                Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_AUTHENTICATED_USER"));
                Authentication endUserAuth = new MyUsernamePasswordGroupAuthenticationToken(user, grantedAuthorities);
                authentication = endUserAuth;
            } else {
                LOGGER.debug("user not found, throwing AuthenticationServiceException....");
                throw new AuthenticationServiceException("CUSTOM AUTHENTICATION FAILED, user not found");
            }
        } 
    } catch (Exception e) {
        LOGGER.debug("Exception occurred, rethrowing AuthenticationServiceException....");
        throw new AuthenticationServiceException("CUSTOM AUTHENTICATION FAILED: " + e.getMessage());
    }
    return authentication;
}

另外我使用的弹簧安全配置如下

   <sec:authentication-manager alias="authenticationManager" erase-credentials="false">
        <sec:authentication-provider ref="customAuthenticationProvider"/>
    </sec:authentication-manager>

    <bean id="customAuthenticationProvider"
          class="com.example.security.MyAuthenticationProvider"/>
    </bean>

但是我猜你的也没关系,剩下的只是让你的令牌持有名字,组和用户,你应该没事,希望有所帮助

答案 1 :(得分:0)

创建自己的实现AuthenticationProvider的想法非常正确。 那你就有很高的自由度。您甚至可以覆盖DaoAuthenticationProvider并创建自己的MyDaoAuthenticationProvider版本。

然而,更简洁的方法就是创建一个合适的Principal对象。您的用户名和组看起来像复合用户名:

username = requestUsername + delimiter + requestGroup; 

然后在这种情况下,可以扩展UsernamePasswordAuthenticationFilter来创建一个正确的Principal对象并将其放入UsernamePasswordAuthenticationToken中,这样Principal的getName()方法返回复合用户名,然后在MyUserDetailsS​​ervice中将它再次拆分回requestUsername和requestGroup并执行repo / dao / jdbc,用于获取用户详细信息。

答案 2 :(得分:0)

您可能已经了解,Spring Security身份验证中没有组或域。

解决此问题的一种方法是将组名附加到用户名(例如使用斜杠分隔符),因此内部用户名为“username / group”。

如果您仍希望用户名和组在登录表单中是单独的字段,则可以执行此类自定义身份验证过滤器以连接用户名和组:

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    protected String obtainUsername(HttpServletRequest request) {
        String username = super.obtainUsername(request);
        String group = request.getParameter("group");
        username += "/" + group;
        return username;
    }
}

然后在你的UserDetailsService中,你需要将它们分开:

public UserDetails loadUserByUsername(String username) {
    int index = username.indexOf("/");
    String group = username.substring(index + 1);
    username = username.substring(0, index);
    // find the user by username and group
}