自定义登录方式
Spring Security,默认实现为账号密码登录
怎么新增登录方式呢?例如:手机号+ 验证码 ,微信登录
以下示例:手机号+ 验证码登录
涉及到的接口
#用于定义token
org.springframework.security.authentication.AbstractAuthenticationToken;
#用于身份验证
org.springframework.security.authentication.AuthenticationProvider;
#用于得到授权对象
org.springframework.security.core.userdetails.UserDetailsService;
#Security 配置类
org.springframework.security.core.userdetails.WebSecurityConfigurerAdapter;
# 1:定义Token
/**
* 验证码认证Token
*/
public class CaptchaAuthenticationToken extends AbstractAuthenticationToken {
private final Object phone;
private String captcha;
/**
* 此构造函数用来初始化未授信凭据.
*
* @param phone 手机号
* @param captcha 验证码
*/
public CaptchaAuthenticationToken(Object phone, String captcha) {
super(null);
this.phone = phone;
this.captcha = captcha;
setAuthenticated(false);
}
/**
* 此构造函数用来初始化授信凭据.
* @param phone 手机号
* @param captcha 验证码
* @param authorities 认证
*/
public CaptchaAuthenticationToken(Object phone, String captcha, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.phone = phone;
this.captcha = captcha;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.captcha;
}
public Object getPrincipal() {
return this.phone;
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
captcha = null;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
}
# 2:身份认证
public class CaptchaAuthenticationProvider implements AuthenticationProvider {
private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
private final UserDetailsService userDetailsService;
private final ISmsService smsService;
/**
* 定义构造器
* @param userDetailsService userDetailsService
* @param smsService 短信 Service
*/
public CaptchaAuthenticationProvider(UserDetailsService userDetailsService, ISmsService smsService) {
this.userDetailsService = userDetailsService;
this.smsService = smsService;
}
/**
* 身份认证
* @param authentication 需要认证的Token
* @return 返回认证成功Token / 认证失败则抛出异常
*/
@Override
public Authentication authenticate(Authentication authentication) {
CaptchaAuthenticationToken unAuthenticationToken = (CaptchaAuthenticationToken) authentication;
//得到手机号和 验证码
String phone = unAuthenticationToken.getName();
String rawCode = (String) unAuthenticationToken.getCredentials();
//通过自定义实现得到的用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
//用户信息校验 ,业务逻辑是否校验...
Boolean match;
if (match) {
//验证通过 更新凭证
return createSuccessAuthentication(authentication, userDetails);
} else {
throw new BadCredentialsException("captcha is not matched");
}
}
/**
* 认证成功将非授信凭据转为授信凭据.
* 封装用户信息 角色信息。
* @return the authentication
*/
protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
Collection<? extends GrantedAuthority> authorities = authoritiesMapper.mapAuthorities(user.getAuthorities());
//返回一个 授信凭据
CaptchaAuthenticationToken authenticationToken = new CaptchaAuthenticationToken(user, null, authorities);
authenticationToken.setDetails(authentication.getDetails());
return authenticationToken;
}
@Override
public boolean supports(Class<?> authentication) {
//认证器是否是 CaptchaAuthenticationToken
return CaptchaAuthenticationToken.class.isAssignableFrom(authentication);
}
}
# 3:定义认证对象
/**
* 登录用户身份权限
*
*/
@Data
public class LoginUser implements UserDetails {
private static final long serialVersionUID = 1L;
private String username;
private String password;
private Set<String> permissions;
private Set<String> roles;
//其他用户属性可自行定义
public LoginUser(String username, Set<String> permissions, Set<String> roles) {
this.permissions = permissions;
this.roles = roles;
this.username = username;
//因为是手机号验证码登录,密码不重要
this.password = "";
}
@JSONField(serialize = false)
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}
# 4:配置认证
/**
* 验证码认证配置类
*/
@Slf4j
@Configuration
public class CaptchaAuthenticationConfiguration {
/**
* 实现 UserDetailsService
* 获取用户信息
*/
@Bean
@Qualifier("captchaUserDetailsService")
public UserDetailsService captchaUserDetailsService() {
return phone -> {
SysUser user;
// 获取用户信息 此处省略 ....
//返回一个登录成功的用户 并设置角色,权限
return new LoginUser(username, permissions, roles);
};
}
/**
* 验证码认证器
* @param smsService smsService
* @param userDetailsService userDetailsService
*/
@Bean
public CaptchaAuthenticationProvider captchaAuthenticationProvider(ISmsService smsService,
@Qualifier("captchaUserDetailsService") UserDetailsService userDetailsService
) {
return new CaptchaAuthenticationProvider(userDetailsService, smsService);
}
}
添加到配置
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CaptchaAuthenticationProvider captchaAuthenticationProvider;
//此处省略其他配置。。。
@Override
protected void configure(AuthenticationManagerBuilder auth) {
//添加 CaptchaAuthenticationProvider
auth.authenticationProvider(captchaAuthenticationProvider);
}
}
# 5:使用
@Service
public class LoginService {
/**
* 身份验证管理器
*/
@Resource
private AuthenticationManager authenticationManager;
public String captchaLogin(String phone,String captcha) {
String phone = captchaLoginBody.getPhone();
// 用户验证
Authentication authentication;
try {
//通过验证管理器 去验证 CaptchaAuthenticationToken
// 该方法会去调用 captchaUserDetailsService.loadUserByUsername
authentication = authenticationManager.authenticate(new CaptchaAuthenticationToken(phone, captcha));
} catch (Exception e) {
//认证失败
throw new ServiceException(e.getMessage());
}
//认证成功,得到登录成功用户
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 下面处理自己的业务逻辑,此处省略....
return "登录成功";
}
}
上次更新: 2024/03/05, 13:24:49