感谢:https://blog.csdn.net/qq_33396608/article/details/103271320
自己记录一遍操作步骤,防止原作者文章删除找不到。
事前工作:sys_oauth_client_details表authorized_grant_types字段新增:phone_sms
工具类,获取当前用户:
import com.iwmake.common.core.constant.SystemConstant;
import com.iwmake.common.security.domain.LoginUser;
import com.iwmake.system.api.domain.SysUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.ObjectUtils;
/**
* 权限获取工具类
* @author Dylan
*/
public class SecurityUtils {
/**
* 获取Authentication
*/
public static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* 获取用户
*/
public static String getUsername() {
return getLoginUser().getUsername();
}
/**
* 获取用户
*/
public static LoginUser getLoginUser(Authentication authentication) {
Object principal = authentication.getPrincipal();
if (principal instanceof LoginUser) {
return (LoginUser) principal;
}
return null;
}
/**
* 获取用户
*/
public static LoginUser getLoginUser() {
Authentication authentication = getAuthentication();
if (authentication == null) {
return null;
}
return getLoginUser(authentication);
}
/**
* 生成BCryptPasswordEncoder密码
* @param password 密码
* @return 加密字符串
*/
public static String encryptPassword(String password) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.encode(password);
}
/**
* 判断密码是否相同
* @param rawPassword 真实密码
* @param encodedPassword 加密后字符
* @return 结果
*/
public static boolean matchesPassword(String rawPassword, String encodedPassword) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
少说话,多撸代码:
一、java实体,新增userId方便获取(principal时候方便有Id存在):
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
@ToString
public class LoginUser extends User {
private static final long serialVersionUID = 1L;
/**
* 新增用户ID
*/
private Long userId;
}
二、继承抽象类AbstractTokenGranter
import java.util.Collection;
import java.util.Map;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import com.iwmake.common.security.domain.LoginUser;
/**
*
* 说明:
* 自定义token授予抽象实现
* 继承抽象类AbstractTokenGranter,我们也是抽象的,后面好扩展
* @author:heshengjin qq:2356899074
* @date 2021年2月5日 上午9:51:00
*/
public abstract class CustomAbstractTokenGranter extends AbstractTokenGranter {
public CustomAbstractTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = tokenRequest.getRequestParameters();
LoginUser loginUser = (LoginUser)getUserDetails(parameters);
if (null == loginUser) {
throw new InvalidGrantException("账户未找到");
}
//Object principal, Object credentials,Collection extends GrantedAuthority> authorities
Authentication userAuth = new UsernamePasswordAuthenticationToken(loginUser,
loginUser.getPassword(), loginUser.getAuthorities());
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
/**
* 自定义获取用户信息
*/
protected abstract UserDetails getUserDetails(Map<String, String> parameters);
}
三、定义短信验证码具体逻辑
import java.util.Map;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
/**
*
* 说明:
* 手机号-短信验证码
* 定义短信验证码具体逻辑
* @author:heshengjin qq:2356899074
* @date 2021年2月5日 上午9:51:34
*/
public class PhoneSmsTokenGranter extends CustomAbstractTokenGranter {
private static final String PHONE_SMS = "phone_sms";
private CustomUserDetailService customUserDetailService;
public PhoneSmsTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, CustomUserDetailService customUserDetailService) {
super(tokenServices, clientDetailsService, requestFactory, PHONE_SMS);
this.customUserDetailService = customUserDetailService;
}
@Override
protected UserDetails getUserDetails(Map<String, String> parameters) {
String phone = parameters.get("phone");
String smsCode = parameters.get("sms_code");
return customUserDetailService.loadByPhone(phone,smsCode);
}
}
四、验证部分
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.iwmake.common.core.domain.RestResult;
import com.iwmake.common.core.enums.UserStatus;
import com.iwmake.common.core.exception.BaseException;
import com.iwmake.common.core.utils.StringUtils;
import com.iwmake.common.security.domain.LoginUser;
import com.iwmake.system.api.RemoteUserService;
import com.iwmake.system.api.domain.SysUser;
import com.iwmake.system.api.model.UserInfo;
import lombok.extern.slf4j.Slf4j;
/**
*
* 说明:自定义的获取用户信息
* @author:heshengjin qq:2356899074
* @date 2021年2月5日 上午9:51:52
*/
@Service
@Slf4j
public class CustomUserDetailService {
//这里是数据库查询用户操作逻辑代码
@Autowired
private RemoteUserService remoteUserService;
public UserDetails loadByPhone(String phone, String code) {
//在验证手机号和验证码--load from redis
// if (!"123456".equals(code)) {
// throw new InvalidGrantException("验证码错误或已过期");
// }
// return new User(phone, "", AuthorityUtils.createAuthorityList("user:add", "user:delete"));
//查询数据库验证
RestResult<UserInfo> userResult = remoteUserService.getUserInfoByPhone(phone);
checkUser(userResult, phone);
return getUserDetails(userResult);
}
//自定义其他登录等
public void checkUser(RestResult<UserInfo> userResult, String phone) {
if (ObjectUtils.isEmpty(userResult) || ObjectUtils.isEmpty(userResult.getData())) {
log.info("登录手机号:{} 不存在.", phone);
throw new UsernameNotFoundException("登录手机号:" + phone + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag())) {
log.info("登录手机号:{} 已被删除注销.", phone);
throw new BaseException("对不起,您的手机号:" + phone + " 已被删除注销");
} else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus())) {
log.info("登录手机号:{} 已被停用.", phone);
throw new BaseException("对不起,您的手机号:" + phone + " 已停用");
}
}
private UserDetails getUserDetails(RestResult<UserInfo> result) {
UserInfo info = result.getData();
Set<String> dbAuthsSet = new HashSet<String>();
if (StringUtils.isNotEmpty(info.getRoles())) {
// 获取角色
dbAuthsSet.addAll(info.getRoles());
// 获取权限
dbAuthsSet.addAll(info.getPermissions());
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
SysUser user = info.getSysUser();
System.out.println("==========CustomUserDetailServiceImpl========");
System.out.println(JSON.toJSONString(user));
return new LoginUser(user.getUserId(),user.getSuperAdmin(),user.getSuperTenant(), user.getTenantId(),user.getDeptId(),user.getUserName(), user.getPassword(), true, true, true, true,
authorities);
// return new User(user.getUserName(), user.getPassword(), true, true, true, true,
// authorities);
}
}
五、在你的授权服务器@EnableAuthorizationServerl类加入自定义的token授予模式
@Autowired
private CustomUserDetailService customUserDetailService;
/**
* 定义授权(authorization)和令牌端点(token)以及令牌服务(token services)
*
* @param endpoints 配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
//...
//在原有的基础上增加自定义手机号短信登录
List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
tokenGranters.add(endpoints.getTokenGranter());
endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
}
/**
* 自定义TokenGranter集合
*/
private List<TokenGranter> getTokenGranters(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
return new ArrayList<>(Collections.singletonList(
new PhoneSmsTokenGranter(tokenServices, clientDetailsService, requestFactory, customUserDetailService)
));
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@GetMapping("getInfo")
public Result getInfo() {
//当前登录用户ID
Long userId = SecurityUtils.getLoginUser().getUserId();
// 角色集合
Set<String> roles = permissionService.getRolePermission(userId);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(userId);
Result ajax = Result.success();
ajax.put("user", userService.selectUserById(userId));
ajax.put("roles", roles);
ajax.put("permissions", permissions);
return ajax;
}