springsecurity配置多种登录方式,比如手机验证码登录、邮箱登录、微信小程序登录等,下面就以微信小程序登录为例进行讲解。
package com.school.information.core.security.entity;
import com.alibaba.fastjson.annotation.JSONField;
import com.school.information.entity.SysWechatUserEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WechatAppUser implements UserDetails {
/**
* 微信小程序返回的session_key
*/
private String sessionKey;
/**
* 小程序的基本信息
*
* @return
*/
private SysWechatUserEntity sysWechatUser;
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
@JSONField(serialize = false)
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return sysWechatUser.getOpenid();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
package com.school.information.core.service;
import com.school.information.core.exception.ServiceException;
import com.school.information.core.security.entity.SecurityUser;
import com.school.information.entity.SysUserEntity;
import com.school.information.enums.result.SysResultEnum;
import com.school.information.enums.status.EnabledEnum;
import com.school.information.service.SysMenuService;
import com.school.information.service.SysRoleService;
import com.school.information.service.SysUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Objects;
import java.util.Set;
@Service
@Slf4j
public class SecurityUserServiceImpl implements UserDetailsService {
@Resource
private SysUserService sysUserService;
@Resource
private SysMenuService menuService;
@Resource
private SysRoleService roleService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("##进入后台用户登录逻辑代码处理:{}", username);
// 根据登录名获取用户信息
SysUserEntity sysUser = sysUserService.getByPhone(username);
if (Objects.isNull(sysUser)) {
log.error("##登录用户:{}不存在", username);
throw new ServiceException(SysResultEnum.USER_NAME_NO_EXISTED);
} else if (EnabledEnum.DISABLE.equals(sysUser.getIsEnabled())) {
log.error("##登录用户:{}已停用", username);
throw new ServiceException(SysResultEnum.USER_DISABLED);
}
// 获取用户角色、权限信息 用户角色和权限信息保留一个即可
SecurityUser securityUser = new SecurityUser();
securityUser.setSysUser(sysUser);
Set perms = menuService.findMenuByUserId(sysUser.getId());
securityUser.setPermissions(perms);
Set roles = roleService.findRoleByUserId(sysUser.getId());
securityUser.setRoles(roles);
return securityUser;
}
}
package com.school.information.core.security.authen;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
import java.util.Collection;
/**
* 微信小程序登录token验证,模仿 WechatAppAuthenticationToken 重写编写
*/
public class WechatAppAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 580L;
private final Object principal;
private Object credentials;
public WechatAppAuthenticationToken(Object principal, Object credentials) {
super((Collection) null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public WechatAppAuthenticationToken(Object principal, Object credentials, Collection extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
public static WechatAppAuthenticationToken unauthenticated(Object principal, Object credentials) {
return new WechatAppAuthenticationToken(principal, credentials);
}
public static WechatAppAuthenticationToken authenticated(Object principal, Object credentials, Collection extends GrantedAuthority> authorities) {
return new WechatAppAuthenticationToken(principal, credentials, authorities);
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
super.setAuthenticated(false);
}
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
package com.school.information.core.security.provider;
import cn.hutool.core.util.ObjectUtil;
import com.school.information.core.security.authen.WechatAppAuthenticationToken;
import com.school.information.core.security.entity.WechatAppUser;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* 微信小程序登录验证, 模仿 DaoAuthenticationProvider
*/
public class WechatAppUserAuthenticationProvider implements AuthenticationProvider {
private UserDetailsService userDetailsService;
public WechatAppUserAuthenticationProvider() {
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 自定义小程序认证登录 根据小程序openid获取小程序用户信息
WechatAppUser wechatAppUser = (WechatAppUser) userDetailsService.loadUserByUsername(authentication.getName());
if (ObjectUtil.isEmpty(wechatAppUser)) {
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
}
// 注意要调用 authenticated() 方法 这个是认证通过了的
WechatAppAuthenticationToken result = WechatAppAuthenticationToken.authenticated(wechatAppUser, authentication.getCredentials(), null);
result.setDetails(authentication.getDetails());
return result;
}
public boolean supports(Class> authentication) {
return WechatAppAuthenticationToken.class.isAssignableFrom(authentication);
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
protected UserDetailsService getUserDetailsService() {
return this.userDetailsService;
}
}