:security实现多种方式登陆
spring security 的核心功能主要包括
认证
授权
攻击防护
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
在说多种认证方式之前,咱们先简单过下单认证方式是如何配置的,也说下Spring Security的各个配置类的作用。
1、UsernamePasswordAuthenticationFilter
Spring Security 默认认证过滤器,处于拦截器链当中,继承AbstractAuthenticationProcessingFilter,咱们看一下源码
可以看出里面构造方法指定了默认拦截地址 /login,attemptAuthentication是父类AbstractAuthenticationProcessingFilter抽象方法的实现方法,在父类中doFilter方法里调用,可以看到方法实现是从request里取得用户名密码,最后构建成UsernamePasswordAuthenticationToken,然后调用AuthenticationManager的 authenticate 方法作为参数传进去进行认证。
2、UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken没什么好讲的,在其实就是对认证参数用户名密码的封装,当然后续登录成功之后会作为用户认证信息的封装。
3、AuthenticationManager
authenticationManager是AbstractAuthenticationProcessingFilter的一个成员变量,从上面可以看出,这个参数是必须赋值的,采用默认的过滤器认证,spring security会默认给一个实现类ProviderManager,看下代码
从源码看到,管理器会遍历所有注册的认证器集合,调用每个认证器的authenticate认证,此时会有疑惑,如果多个登录方式,肯定会有多个认证器,每次都遍历认证所有的认证器是否不太合理?关键在于以下这个判断代码
这个 toTest 参数就是过滤器传进来的 UsernamePasswordAuthenticationToken
Class<? extends Authentication> toTest = authentication.getClass();
if (!provider.supports(toTest)) {
continue;
}
会调用每个认证器的supports方法,只有此方法返回true,才会执行认证,(由此想到如果自定义认证器,此方法一定要重写),此方法如何实现,咱们看一下此方法的默认实现,会判断
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
由此看出,参数必须为 UsernamePasswordAuthenticationToken 类或者其子类的字节码,此参数又是由UsernamePasswordAuthenticationFilter 里传过来的
由此得知,每个过滤器都需要一个AbstractAuthenticationToken的子类绑定
4、AuthenticationProvider
这个是重点配置,具体认证方法都是在这里实现的,因此我们要自定义我们的认证方法,都需要实现这个接口,这个接口只有两个方法,authenticate用来认证,supports 用来决定启用条件
具体实现方法根据自己的业务需要,一般是查询数据库,对比密码,看下我的实现类,一般我们会注入一个自定义的UserDetailService实现类,重写 loadUserByUsername,具体根据用户名查询用户信息,认证成功将用户信息包装成 Authentication 返回
5、AuthenticationSuccessHandler、AuthenticationFailureHandler
看过滤器源码,认证结束后,会根据认证成功或失败,分别调用两个成功失败处理器
successHandler.onAuthenticationSuccess(request, response, authResult);
failureHandler.onAuthenticationFailure(request, response, failed);
因此,我们可以自定义这两个处理器,来自己处理认证成功失败
6、配置文件
最后,Spring Security的配置文件
根据上面的介绍和代码,我们大概可以知道原理了,现在开始撸码吧!
pom文件添加security的依赖
${spring-boot.version} 2.3.1.RELEASE
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
实现UsernamePasswordAuthenticationFilter
该步骤作为过滤器适用,根据判断是否存在对应session,以提示是否需要重新登陆
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private static final List<String> forIndexList = Lists.newArrayList();
static {
forIndexList.add("/");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//根路径,直接跳转到index页面
if (forIndexList.contains(request.getServletPath())) {
response.sendRedirect(request.getRequestURL() + "index.html");
return;
}
log.info("===>URL={} , IP={}", request.getRequestURL(), request.getRemoteAddr());
// 如果是接口请求验证登录情况
if (!request.getRequestURL().toString().contains(".html")
&& !"/login".equals(request.getServletPath())
&& !"/mobileLogin".equals(request.getServletPath())
&& !"/wxLogin".equals(request.getServletPath())) {
//用户是否登录
LoginUserDetail currentUser = SessionUtils.getCurrentUser(request);
log.info("current user {}", JSON.toJSONString(currentUser));
if (currentUser == null) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter printWriter = response.getWriter();
Response httpResponse = new Response(401, "请先登录!");
printWriter.write(JSON.toJSONString(httpResponse));
printWriter.flush();
printWriter.close();
return;
}
}
chain.doFilter(request, response);
}
}
实现UserDetails,作为session的信息实体
public class LoginUserDetail implements UserDetails, Serializable {
private Long id;
private String username;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
登陆方式1:账号密码登陆
public class UsernameAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public UsernameAuthenticationProcessingFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String code = request.getParameter("code");
String kaptchaCode = (String) request.getSession().getAttribute("verificationCode");
if(StringUtils.isEmpty(code)) {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(500);
PrintWriter printWriter = response.getWriter();
Response httpResponse = new Response(500,"请填写验证码!");
printWriter.write(new ObjectMapper().writeValueAsString(httpResponse));
printWriter.flush();
printWriter.close();
throw new AuthenticationServiceException("请填写验证码");
}else if(!kaptchaCode.toLowerCase().equals(code.toLowerCase())) {
logger.info("goto===>验证码错误");
response.setContentType("application/json;charset=UTF-8");
response.setStatus(500);
PrintWriter printWriter = response.getWriter();
Response httpResponse = new Response(500,"验证码错误!");
printWriter.write(new ObjectMapper().writeValueAsString(httpResponse));
printWriter.flush();
printWriter.close();
throw new AuthenticationServiceException("验证码错误");
}
// 用户名称
String loginAcct = obtainUsername(request);
if (StringUtils.isEmpty(loginAcct)) {
throw new AuthenticationServiceException("登录账号不能为空");
}
// 密码
String password = obtainPassword(request);
if (StringUtils.isEmpty(password)) {
throw new AuthenticationServiceException("密码不能为空");
}
return this.getAuthenticationManager().authenticate(new UsernameAuthenticationToken(loginAcct, password));
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
}
public class UsernameAuthenticationProvider implements AuthenticationProvider {
protected Log logger = LogFactory.getLog(getClass());
// 自定义查询用户信息
@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
UsernameAuthenticationToken tokenReq = (UsernameAuthenticationToken) authentication;
//todo 根据手机号码,查找登录人信息....
UserDetails userDetails = userDetailsService.loadUserByUsername(tokenReq.getUsername());
//todo 密码加密后比较是否匹配
if(!passwordEncoder.matches(tokenReq.getPassword(), userDetails.getPassword())) {
throw new BadCredentialsException("账号或者密码错误");
}
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
} catch (BadCredentialsException | UsernameNotFoundException e) {
logger.error(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new BadCredentialsException("登录异常");
}
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernameAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class UsernameAuthenticationToken extends AbstractAuthenticationToken {
private String username;
private String password;
private String code;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public UsernameAuthenticationToken(String username, String password) {
super(null);
this.username = username;
this.password = password;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class MobileUserDetailsServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService {
@SneakyThrows
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 自定义查询用户信息
return new LoginUserDetail();
}
}
登陆方式2:手机号登陆
本人使用了阿里云短信验证码,openId和unionId为微信登陆做准备
public class MobileCodeAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "mobile";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "code";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
public static final String SPRING_SECURITY_FORM_UNION_KEY = "unionId";
public static final String SPRING_SECURITY_FORM_OPEN_KEY = "openId";
private String unionParameter = SPRING_SECURITY_FORM_UNION_KEY;
private String openParameter = SPRING_SECURITY_FORM_OPEN_KEY;
private boolean postOnly = true;
public MobileCodeAuthenticationProcessingFilter() {
super(new AntPathRequestMatcher("/mobileLogin", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// 电话号码
String mobile = obtainUsername(request);
if (StringUtils.isEmpty(mobile)) {
throw new AuthenticationServiceException("电话号码不能为空");
}
// 验证码
String code = obtainPassword(request);
if (StringUtils.isEmpty(code)) {
throw new AuthenticationServiceException("验证码不能为空");
}
return this.getAuthenticationManager().authenticate(new MobileCodeAuthenticationToken(mobile, code, obtainUnionId(request), obtainOpenId(request)));
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUnionId(HttpServletRequest request) {
return request.getParameter(unionParameter);
}
protected String obtainOpenId(HttpServletRequest request) {
return request.getParameter(openParameter);
}
}
@Slf4j
public class MobileCodeAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
protected Log logger = LogFactory.getLog(getClass());
@Autowired
@Qualifier("mobileUserDetailsServiceImpl")
private UserDetailsService userDetailsService;
@DubboReference
ISysUserService sysUserService;
@DubboReference
ISmsCodeService smsCodeService;
@DubboReference
IUserWxService userWxService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MobileCodeAuthenticationToken tokenReq = (MobileCodeAuthenticationToken) authentication;
log.info("Method:authenticate. tokenReq:{}", FastJsonUtils.convertObjectToJSON(tokenReq));
try {
SysUser sysUser = sysUserService.findUserByMobile(tokenReq.getMobile());
if (sysUser == null) {
throw new BadCredentialsException("用户不存在");
}
SmsCode smsCode = smsCodeService.getSmsCode(tokenReq.getMobile(), SmsCodeUserType.B.getId());
if (smsCode == null) {
throw new BadCredentialsException("请获取验证码");
}
if (LocalDateTime.now().isAfter(smsCode.getModifyTime().plusMinutes(5))) {
throw new BadCredentialsException("验证码已过期,请重新获取验证码");
}
// 校验验证码是否匹配
if (!tokenReq.getCode().equals(smsCode.getCode())) {
throw new BadCredentialsException("验证码不正确,请检查后重新输入");
}
// 根据手机号码,获取登录账号
UserDetails userDetails = userDetailsService.loadUserByUsername(smsCode.getUserId().toString());
if (StringUtils.isNotBlank(tokenReq.getUnionId())) {
UserWx userWx = userWxService.findByUnionId(tokenReq.getUnionId());
if (userWx == null) {
userWxService.createUserWx(UserWx.builder().userId(smsCode.getUserId()).unionId(tokenReq.getUnionId()).openId(tokenReq.getOpenId()).build());
} else {
userWx.setOpenId(tokenReq.getOpenId());
userWx.setUnionId(tokenReq.getUnionId());
userWx.setUserId(smsCode.getUserId());
userWxService.updateUserWx(userWx);
}
}
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
} catch (BadCredentialsException | UsernameNotFoundException e) {
logger.error(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new BadCredentialsException("手机验证码登录异常");
}
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return (MobileCodeAuthenticationToken.class.isAssignableFrom(authentication));
}
public class MobileCodeAuthenticationToken extends AbstractAuthenticationToken {
private String mobile;
private String code;
private String unionId;
private String openId;
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public MobileCodeAuthenticationToken(String mobile, String code) {
super(null);
this.mobile = mobile;
this.code = code;
}
public MobileCodeAuthenticationToken(String mobile, String code, String unionId, String openId) {
super(null);
this.mobile = mobile;
this.code = code;
this.unionId = unionId;
this.openId = openId;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
@Service
@Slf4j
public class MobileUserDetailsServiceImpl implements org.springframework.security.core.userdetails.UserDetailsService
@SneakyThrows
@Override
public UserDetails loadUserByUsername(String phone) throws UsernameNotFoundException {
// 自定义返回结果
return new UserDetails();
}
}
登陆方式3:微信登陆
对接微信扫码登陆文档可参考https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
微信登陆流程如下
public class WxAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_UNION_KEY = "unionId";
public static final String SPRING_SECURITY_FORM_OPEN_KEY = "openId";
private String unionParameter = SPRING_SECURITY_FORM_UNION_KEY;
private String openParameter = SPRING_SECURITY_FORM_OPEN_KEY;
private boolean postOnly = true;
public WxAuthenticationProcessingFilter() {
super(new AntPathRequestMatcher("/wxLogin", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
// unionId
String unionId = obtainUnion(request);
if (StringUtils.isEmpty(unionId)) {
throw new AuthenticationServiceException("unionId不能为空");
}
// openId
String openId = obtainOpen(request);
return this.getAuthenticationManager().authenticate(new WxAuthenticationToken(unionId, openId));
}
protected String obtainUnion(HttpServletRequest request) {
return request.getParameter(unionParameter);
}
protected String obtainOpen(HttpServletRequest request) {
return request.getParameter(openParameter);
}
}
@Slf4j
public class WxAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
protected Log logger = LogFactory.getLog(getClass());
@Autowired
@Qualifier("mobileUserDetailsServiceImpl")
private UserDetailsService userDetailsService;
@DubboReference
IUserWxService userWxService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WxAuthenticationToken tokenReq = (WxAuthenticationToken) authentication;
try {
UserWx userWx = userWxService.findByUnionId(tokenReq.getUnionId());
if (userWx == null) {
throw new BadCredentialsException("微信账号未绑定");
}
UserDetails userDetails = userDetailsService.loadUserByUsername(userWx.getUserId().toString());
log.info("Method:authenticate. userDetails:{}", FastJsonUtils.convertObjectToJSON(userDetails));
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
} catch (BadCredentialsException | UsernameNotFoundException e) {
log.error("Method:authenticate. Exception:", e);
throw e;
} catch (Exception e) {
log.error("Method:authenticate. Exception:", e);
throw new BadCredentialsException("微信登录异常");
}
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return (WxAuthenticationToken.class.isAssignableFrom(authentication));
}
}
public class WxAuthenticationToken extends AbstractAuthenticationToken {
private String unionId;
private String openId;
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public WxAuthenticationToken(String unionId, String openId) {
super(null);
this.unionId = unionId;
this.openId = openId;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
}
最后进行security的配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
LoginAuthenticationEntryPoint loginAuthenticationEntryPoint; // 未登陆时返回 JSON 格式的数据给前端(否则为 html)
@Autowired
LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler; // 登录成功返回的 JSON 格式数据给前端(否则为 html)
@Autowired
LoginAuthenticationFailureHandler loginAuthenticationFailureHandler; // 登录失败返回的 JSON 格式数据给前端(否则为 html)
@Autowired
LoginLogoutSuccessHandler loginlogoutSuccessHandler; // 注销成功返回的 JSON 格式数据给前端(否则为 登录时的 html)
@Autowired
LoginAccessDeniedHandler loginAccessDeniedHandler; // 无权访问返回的 JSON 格式数据给前端(否则为 403 html 页面)
@Autowired
LoginInvalidSessionStrategy loginInvalidSessionStrategy;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(mobileCodeAuthenticationProvider()).authenticationProvider(usernameAuthenticationProvider()).authenticationProvider(wxAuthenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//这里是在校验密码之前先校验验证码,如果验证码错误就不检验用户名和密码了
http.addFilterBefore(new LoginFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(mobileCodeAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(usernameAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(wxAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/**/index.html").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/mobileLogin/**").permitAll()
.antMatchers("/wxLogin/**").permitAll()
.anyRequest()
.permitAll()
.and().formLogin()
.permitAll()
.and()
.logout().logoutSuccessHandler(loginlogoutSuccessHandler).permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(loginAuthenticationEntryPoint)
.and().csrf().disable()
.cors().disable();
}
/**
* 跨域配置
* @return 基于URL的跨域配置信息
*/
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 注册跨域配置
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public MobileCodeAuthenticationProcessingFilter mobileCodeAuthenticationProcessingFilter() throws Exception {
MobileCodeAuthenticationProcessingFilter filter = new MobileCodeAuthenticationProcessingFilter();
filter.setAuthenticationManager(super.authenticationManagerBean());
filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
return filter;
}
@Bean
public UsernameAuthenticationProcessingFilter usernameAuthenticationProcessingFilter() throws Exception {
UsernameAuthenticationProcessingFilter filter = new UsernameAuthenticationProcessingFilter();
filter.setAuthenticationManager(super.authenticationManagerBean());
filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
return filter;
}
@Bean
public WxAuthenticationProcessingFilter wxAuthenticationProcessingFilter() throws Exception {
WxAuthenticationProcessingFilter filter = new WxAuthenticationProcessingFilter();
filter.setAuthenticationManager(super.authenticationManagerBean());
filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler());
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler());
return filter;
}
@Bean
public MobileCodeAuthenticationProvider mobileCodeAuthenticationProvider() {
return new MobileCodeAuthenticationProvider();
}
@Bean
public UsernameAuthenticationProvider usernameAuthenticationProvider() {
return new UsernameAuthenticationProvider();
}
@Bean
public WxAuthenticationProvider wxAuthenticationProvider() {
return new WxAuthenticationProvider();
}
@Bean
public LoginAuthenticationFailureHandler myAuthenticationFailureHandler() {
return new LoginAuthenticationFailureHandler();
}
@Bean
public LoginAuthenticationSuccessHandler myAuthenticationSuccessHandler() {
return new LoginAuthenticationSuccessHandler();
}
}
可以实现AuthenticationFailureHandler重写onAuthenticationFailure来对登陆异常进行 统一的处理。可以实现AuthenticationSuccessHandler重写onAuthenticationSuccess对登陆成功进行统一处理
这样就完成了三种登陆方式了
部分概念及原理学习于https://blog.csdn.net/qq_36521507/article/details/103365805