在前面的文章《实战Spring Cloud Oauth2系列(三)自定义查询用户的数据结构》和《实战Spring Cloud Oauth2系列(四)自定义查询客户端的数据结构》我们介绍了这两个方法仅仅是通过单一的用户名或者单一的clientid来查询对应的信息的,但是如果用户输出了错误的client信息或者错误的用户名或者密码,那么此时我们就没法去做判断了,此时就完全交由框架来进行异常的处理。这里我们列举下,我们输入错误的clientid,或者错误的用户名密码,那么请求后返回的结果如下:
这时候返回的结果示例如下:
{ "errorCode": 400, "errorMsg": "Bad credentials", "data": null }
所以这就犯愁了,原想着有异常的话,我们通过改写异常来处理错误信息,但是这里没有异常,我们也没其他地方处理这种错误信息,那么怎么办呢?在网上查了一些资料,看到一个可以自定义实现AuthenticationProvider的方法。下面我们来演示一下:
一、创建一个LoginAuthenticationProvider的类,继承自DaoAuthenticationProvider,在这里我们来重写这里的方法,完整示例代码如下:
package org.shop.oauth.server.provider; import javax.annotation.PostConstruct; import org.shop.oauth.server.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class LoginAuthenticationProvider extends DaoAuthenticationProvider{ @Autowired private UserService userService; @Autowired private PasswordEncoder passwordEncoder; @Autowired private void setJdbcUserDetailsService() { setUserDetailsService(userService); } @PostConstruct public void initProvider() { ReloadableResourceBundleMessageSource localMessageSource = new ReloadableResourceBundleMessageSource(); localMessageSource.setBasenames("messages_zh_CN"); messages = new MessageSourceAccessor(localMessageSource); } @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { if (authentication.getCredentials() == null) { logger.debug("Authentication failed: no credentials provided"); throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } String presentedPassword = authentication.getCredentials().toString(); if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { logger.debug("Authentication failed: password does not match stored value"); throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } } }
二、在src/main/resources目录下创建一个messages_zh_CN.properties文件,然后把内容进行翻译下
这里我们把下面的内容复制进去
AbstractAccessDecisionManager.accessDenied=不允许访问 AbstractLdapAuthenticationProvider.emptyPassword=坏的凭证 AbstractSecurityInterceptor.authenticationNotFound=未在SecurityContext中查找到认证对象 AbstractUserDetailsAuthenticationProvider.badCredentials=坏的凭证 AbstractUserDetailsAuthenticationProvider.credentialsExpired=用户凭证已过期 AbstractUserDetailsAuthenticationProvider.disabled=用户已失效 AbstractUserDetailsAuthenticationProvider.expired=用户帐号已过期 AbstractUserDetailsAuthenticationProvider.locked=用户帐号已被锁定 AbstractUserDetailsAuthenticationProvider.onlySupports=仅仅支持UsernamePasswordAuthenticationToken AccountStatusUserDetailsChecker.credentialsExpired=用户凭证已过期 AccountStatusUserDetailsChecker.disabled=用户已失效 AccountStatusUserDetailsChecker.expired=用户帐号已过期 AccountStatusUserDetailsChecker.locked=用户帐号已被锁定 AclEntryAfterInvocationProvider.noPermission=给定的Authentication对象({0})根本无权操控领域对象({1}) AnonymousAuthenticationProvider.incorrectKey=展示的AnonymousAuthenticationToken不含有预期的key BindAuthenticator.badCredentials=坏的凭证 BindAuthenticator.emptyPassword=坏的凭证 CasAuthenticationProvider.incorrectKey=展示的CasAuthenticationToken不含有预期的key CasAuthenticationProvider.noServiceTicket=未能够正确提供待验证的CAS服务票根 ConcurrentSessionControlAuthenticationStrategy.exceededAllowed=已经超过了当前主体({0})被允许的最大会话数量 DigestAuthenticationFilter.incorrectRealm=响应结果中的Realm名字({0})同系统指定的Realm名字({1})不吻合 DigestAuthenticationFilter.incorrectResponse=错误的响应结果 DigestAuthenticationFilter.missingAuth=遗漏了针对'auth' QOP的、必须给定的摘要取值; 接收到的头信息为{0} DigestAuthenticationFilter.missingMandatory=遗漏了必须给定的摘要取值; 接收到的头信息为{0} DigestAuthenticationFilter.nonceCompromised=Nonce令牌已经存在问题了,{0} DigestAuthenticationFilter.nonceEncoding=Nonce未经过Base64编码; 相应的nonce取值为 {0} DigestAuthenticationFilter.nonceExpired=Nonce已经过期/超时 DigestAuthenticationFilter.nonceNotNumeric=Nonce令牌的第1部分应该是数字,但结果却是{0} DigestAuthenticationFilter.nonceNotTwoTokens=Nonce应该由两部分取值构成,但结果却是{0} DigestAuthenticationFilter.usernameNotFound=用户名{0}未找到 JdbcDaoImpl.noAuthority=没有为用户{0}指定角色 JdbcDaoImpl.notFound=未找到用户{0} LdapAuthenticationProvider.badCredentials=坏的凭证 LdapAuthenticationProvider.credentialsExpired=用户凭证已过期 LdapAuthenticationProvider.disabled=用户已失效 LdapAuthenticationProvider.expired=用户帐号已过期 LdapAuthenticationProvider.locked=用户帐号已被锁定 LdapAuthenticationProvider.emptyUsername=用户名不允许为空 LdapAuthenticationProvider.onlySupports=仅仅支持UsernamePasswordAuthenticationToken PasswordComparisonAuthenticator.badCredentials=坏的凭证 PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack. ProviderManager.providerNotFound=未查找到针对{0}的AuthenticationProvider RememberMeAuthenticationProvider.incorrectKey=展示RememberMeAuthenticationToken不含有预期的key RunAsImplAuthenticationProvider.incorrectKey=展示的RunAsUserToken不含有预期的key SubjectDnX509PrincipalExtractor.noMatching=未在subjectDN\: {0}中找到匹配的模式 SwitchUserFilter.noCurrentUser=不存在当前用户 SwitchUserFilter.noOriginalAuthentication=不能够查找到原先的已认证对象
三、修改WebSecurityConfig文件,在configure方法里面添加如下的代码:
auth.authenticationProvider(loginAuthenticationProvider); super.configure(auth);
此时WebSecurityConfig类的完整代码如下:
package org.shop.oauth.server.config; import org.shop.oauth.server.provider.LoginAuthenticationProvider; import org.shop.oauth.server.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * SpringSecurity配置 * */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private LoginAuthenticationProvider loginAuthenticationProvider; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/js/**", "/css/**", "/data/**", "/img/**", "/less/**", "/plugins/**", "/sass/**", "/scss/**", "/logout/**") .permitAll().anyRequest().authenticated() .and().formLogin() .loginPage("/login") .permitAll() .and().logout().logoutUrl("/logout").permitAll(); } @Autowired private UserService userService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(loginAuthenticationProvider); auth.userDetailsService(userService).passwordEncoder(password()); super.configure(auth); } @Bean public PasswordEncoder password() { return new BCryptPasswordEncoder(); } /** * 重新实例化bean */ @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
以上我们就修改完毕了,接着我们启动下项目,重新输入错误信息进行测试下
可以看到提示被修改了。这也就是我们想要的效果。
备注:
1、这里千万记得一定要在WebSecurityConfig类的config方法里面添加如下语句
super.configure(auth);
如果没有添加这条语句,那么是不生效的。
最后按照惯例,附上本案例的源码,登录后即可下载
还没有评论,来说两句吧...