我正在尝试使用自定义过滤器实现Spring安全授权。
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());
}
}
当我尝试登录时,它会进入CustomUserDetailsService并成功从数据库中检索用户详细信息。
但它总是访问authenticationFailureHandler,无论凭据是否正确。
(INFO : com.my.org.MyAuthenticationFailureHandler - point-3
)
有人可以帮我这个吗?感谢
答案 0 :(得分:3)
您将身份验证提供程序配置为使用sha
密码编码(<password-encoder hash="sha"/>
),这意味着它将对传入登录请求中显示的密码进行sha-encode,并将该编码值与密码进行比较存储在UserDetails
对象中(因此预期也会进行sha编码)。在UserDetails
中创建CustomUserDetailsService.loadUserByUsername()
对象时,将从存储库加载用户,并使用toLowerCase()
转换其密码。现在,你为什么要那样做?该值应该是sha编码的密码。通过转换密码的哈希,您可以保证用户无法使用其原始密码登录。
但即使您以纯文本形式存储密码(在这种情况下应删除password-encoder
配置),为什么要在UserDetails
中将其设为小写?如果你这样做,并且用户将他的密码设置为“秘密”,他以后只能用“秘密”进行身份验证。