Domanda

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>

CustomUserDetailsService

/**
 * 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());
    }
}

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

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

Can anyone please help me on this? Thanks

È stato utile?

Soluzione

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".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top