《Spring Security3》第六章第二部分翻译(自定义AuthenticationProvider)
?
实现自定义的AuthenticationProviderpackage com.packtpub.springsecurity.security;// imports omitted public class SignedUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { private String requestSignature; private static final long serialVersionUID = 3145548673810647886L; /** * Construct a new token instance with the given principal, credentials, and signature. * * @param principal the principal to use * @param credentials the credentials to use * @param signature the signature to use */ public SignedUsernamePasswordAuthenticationToken(String principal, String credentials, String signature) { super(principal, credentials); this.requestSignature = signature; } public void setRequestSignature(String requestSignature) { this.requestSignature = requestSignature; } public String getRequestSignature() { return requestSignature; }}?
// imports omitted public class RequestHeaderProcessingFilter extends AbstractAuthenticationProcessingFilter { private String usernameHeader = "j_username"; private String passwordHeader = "j_password"; private String signatureHeader = "j_signature"; protected RequestHeaderProcessingFilter() { super("/j_spring_security_filter"); } @Override public Authentication attemptAuthentication (HttpServletRequest request,HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String username = request.getHeader(usernameHeader); String password = request.getHeader(passwordHeader); String signature = request.getHeader(signatureHeader); SignedUsernamePasswordAuthenticationToken authRequest = new SignedUsernamePasswordAuthenticationToken (username, password, signature); return this.getAuthenticationManager().authenticate(authRequest); } // getters and setters omitted below}?
<http auto-config="true" ...> <custom-filter ref="requestHeaderFilter" before="FORM_LOGIN_FILTER"/></http>
?你可以看到过滤器代码最后请求AuthenticationManager来进行认证。这将最终委托配置的AuthenticationProvider,它们中的一个要支持检查SignedUsernamePasswordAuthenticationToken。接下来,我们需要书写一个AuthenticationProvider来做这件事情。
实现基于请求头的AuthenticationProviderpackage com.packtpub.springsecurity.security;// imports omitted public class SignedUsernamePasswordAuthenticationProvider extends DaoAuthenticationProvider { @Override public boolean supports(Class<? extends Object> authentication) { return (SignedUsernamePasswordAuthenticationToken .class.isAssignableFrom(authentication)); } @Override protected void additionalAuthenticationChecks (UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { super.additionalAuthenticationChecks (userDetails, authentication); SignedUsernamePasswordAuthenticationToken signedToken = (SignedUsernamePasswordAuthenticationToken) authentication; if(signedToken.getRequestSignature() == null) { throw new BadCredentialsException(messages.getMessage( "SignedUsernamePasswordAuthenticationProvider .missingSignature", "Missing request signature"), isIncludeDetailsObject() ? userDetails : null); }// calculate expected signature if(!signedToken.getRequestSignature() .equals(calculateExpectedSignature(signedToken))) { throw new BadCredentialsException(messages.getMessage ("SignedUsernamePasswordAuthenticationProvider .badSignature", "Invalid request signature"), isIncludeDetailsObject() ? userDetails : null); }}private String calculateExpectedSignature (SignedUsernamePasswordAuthenticationToken signedToken) { return signedToken.getPrincipal() + "|+|" + signedToken.getCredentials(); }}
?你可以看到我们再次扩展了框架中的类DaoAuthenticationProvider,因为有用的数据访问代码仍然需要进行实际的用户密码校验以及通过UserDetailsService加载UserDetails。
<authentication-manager alias="authenticationManager"> <authentication-provider ref= "signedRequestAuthenticationProvider"/> <authentication-provider user-service-ref="jdbcUserService"> <password-encoder ref="passwordEncoder" > <salt-source ref="saltSource"/> </password-encoder> </authentication-provider></authentication-manager>
<bean id="signedRequestAuthenticationProvider" ref="passwordEncoder"/> <property name="saltSource" ref="saltSource"/> <property name="userDetailsService" ref="jdbcUserService"/></bean>
?我们自定义的AuthenticationProvider的bean属性其实都是父类所需要的。这些也都指向了我们在AuthenticationManager的中第二个authentication-provider声明中的那些bean。
?将所有的头信息标示为Enabled,访问这个URL:http://localhost:8080/JBCPPets/j_spring_security_filter,会发现我们能够自动登录系统。你可能也会发现我们给予form的登录还能继续可用,这是因为保留了这两个AuthenticationProvider实现以及过滤器链中对应的过滤器。
如果你能时刻记住它们的角色,当你在开发应用特定的AuthenticationProvider时,会在实现和调试过程中少很多的迷惑。