春季安全;自定义过滤器和用户服务参考无法一起使用 [英] Spring Security; custom-filter and user-service-ref not working together

查看:93
本文介绍了春季安全;自定义过滤器和用户服务参考无法一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用自定义过滤器来实现Spring安全授权.

I'm trying to implement spring security authorization using a custom filter.

security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:p="http://www.springframework.org/schema/p" 
   xmlns:util="http://www.springframework.org/schema/util"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/security 
        http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/util 
        http://www.springframework.org/schema/util/spring-util-3.1.xsd">

    <http pattern="/resources" security="none" />

    <http auto-config="false" use-expressions="true" entry-point- ref="authenticationEntryPoint">
        <custom-filter position="BASIC_AUTH_FILTER" ref="loginFilter"/>
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/favicon.ico" access="permitAll"/>
    </http>

    <beans:bean id="authenticationEntryPoint" class="com.my.org.MyAuthenticationEntryPoint"/>


    <beans:bean id="loginFilter"
      class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <beans:property name="authenticationManager" ref="authenticationManager"/>
        <beans:property name="filterProcessesUrl" value="/j_spring_security_check"/>
        <beans:property name="authenticationSuccessHandler"  >
            <beans:bean class="com.my.org.MyAuthenticationSuccessHandler"/>
    </beans:property>
        <beans:property name="authenticationFailureHandler">
            <beans:bean class="com.my.org.MyAuthenticationFailureHandler"/>
        </beans:property>
    </beans:bean>

    <authentication-manager alias="authenticationManager">
            <authentication-provider user-service-ref="customUserDetailsService">
                    <password-encoder hash="sha"/>
            </authentication-provider>
    </authentication-manager>

</beans:beans>

CustomUserDetailsS​​ervice

/**
 * A custom {@link UserDetailsService} where user information
 * is retrieved from a JPA repository
 */
@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {

private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);

@Autowired
private UserRepository userRepository;

/**
 * Returns a populated {@link UserDetails} object. 
 * The username is first retrieved from the database and then mapped to 
 * a {@link UserDetails} object.
 */
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    try {

        logger.info("username-1-->"+username);
        com.cbr.model.User domainUser = userRepository.findByUsername(username);
        logger.info("domainUser-1-->"+domainUser.getPassword());
        logger.info("role-1-->"+domainUser.getRole().getRole());

        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new User(
                domainUser.getUsername(), 
                domainUser.getPassword().toLowerCase(),
                enabled,
                accountNonExpired,
                credentialsNonExpired,
                accountNonLocked,
                getAuthorities(domainUser.getRole().getRole()));

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

/**
 * Retrieves a collection of {@link GrantedAuthority} based on a numerical role
 * @param role the numerical role
 * @return a collection of {@link GrantedAuthority
 */
public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
    List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
    return authList;
}

/**
 * Converts a numerical role to an equivalent list of roles
 * @param role the numerical role
 * @return list of roles as as a list of {@link String}
 */
public List<String> getRoles(Integer role) {
    List<String> roles = new ArrayList<String>();

    if (role.intValue() == 1) {
        roles.add("ROLE_USER");
        roles.add("ROLE_ADMIN");

    } else if (role.intValue() == 2) {
        roles.add("ROLE_USER");
    }

    return roles;
}

/**
 * Wraps {@link String} roles to {@link SimpleGrantedAuthority} objects
 * @param roles {@link String} of roles
 * @return list of granted authorities
 */
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    for (String role : roles) {
        authorities.add(new SimpleGrantedAuthority(role));
    }
    return authorities;
}

}

MyAuthenticationEntryPoint

public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private Log log = LogFactory.getLog(MyAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

        log.info("point-1");

        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
}

MyAuthenticationSuccessHandler

public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    private Log log = LogFactory.getLog(MyAuthenticationSuccessHandler.class);

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,Authentication authentication) throws IOException, ServletException {
        // This is actually not an error, but an OK message. It is sent to avoid redirects.
        log.info("point-2");
        response.sendError(HttpServletResponse.SC_OK);
    }
}

MyAuthenticationFailureHandler

public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    private Log log = LogFactory.getLog(MyAuthenticationFailureHandler.class);

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException exception) throws IOException, ServletException {
        log.info("point-3");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage());
    }
}

当我尝试登录时,它出现在CustomUserDetailsS​​ervice上,并成功地从数据库中检索了用户详细信息.

When I try to login, it comes to the CustomUserDetailsService and successfully retrieve the user details from data base.

但是,无论凭据是否正确,它始终会访问authenticationFailureHandler. (INFO : com.my.org.MyAuthenticationFailureHandler - point-3)

But it always access authenticationFailureHandler whether credentials are correct or not. (INFO : com.my.org.MyAuthenticationFailureHandler - point-3)

有人可以帮我吗?谢谢

推荐答案

您已将身份验证提供程序配置为使用sha密码编码(<password-encoder hash="sha"/>),这意味着它将对进入的登录名中显示的密码进行编码.请求,然后将该编码值与存储在UserDetails对象中的密码(因此也希望对其进行sha编码)进行比较.在CustomUserDetailsService.loadUserByUsername()中创建UserDetails对象时,将从存储库中加载用户,并使用toLowerCase()转换其密码.现在,为什么要这么做呢?该值应该是经过sha编码的密码.通过转换密码的哈希值,可以确保用户将无法使用其原始密码登录. 但是,即使您将密码存储为纯文本格式(在这种情况下,应删除password-encoder配置),为什么还要在UserDetails中将其设置为小写呢?如果这样做,并且用户将密码设置为秘密",则他以后只能使用秘密"进行身份验证.

You configured the authentication provider to use sha password encoding (<password-encoder hash="sha"/>), which means that it will sha-encode the password presented in the incoming login request, and compare that encoded value with the password stored in the UserDetails object (which is consequently expected to be sha-encoded as well). When you create the UserDetails object in CustomUserDetailsService.loadUserByUsername(), the user is loaded from the repository, and you transform his password with toLowerCase(). Now, why on earth would you do that?? That value is supposed to be an sha-encoded password. By transforming the hash of the password you guarantee that the user won't be able to log in with his original password. But even if you store the password in plain text (in which case the password-encoder config should be removed), why would you make it lowercase in UserDetails? If you do that, and the user sets his password to "Secret", he would later only be able to authenticate with "secret".

这篇关于春季安全;自定义过滤器和用户服务参考无法一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆