com.github.rexsheng.springboot.faster.system.auth.application.AuthServiceImpl Maven / Gradle / Ivy
The newest version!
package com.github.rexsheng.springboot.faster.system.auth.application;
import com.github.rexsheng.springboot.faster.common.constant.CommonConstant;
import com.github.rexsheng.springboot.faster.common.constant.SecurityConstant;
import com.github.rexsheng.springboot.faster.common.utils.JwtUtil;
import com.github.rexsheng.springboot.faster.kaptcha.DefaultTextKaptchaProducer;
import com.github.rexsheng.springboot.faster.kaptcha.Kaptcha;
import com.github.rexsheng.springboot.faster.kaptcha.KaptchaProducer;
import com.github.rexsheng.springboot.faster.kaptcha.configuration.KaptchaProperties;
import com.github.rexsheng.springboot.faster.mail.application.JavaMailService;
import com.github.rexsheng.springboot.faster.system.auth.application.dto.*;
import com.github.rexsheng.springboot.faster.system.auth.application.security.InvalidTokenException;
import com.github.rexsheng.springboot.faster.system.auth.application.security.TokenExpireProperties;
import com.github.rexsheng.springboot.faster.system.auth.domain.*;
import com.github.rexsheng.springboot.faster.system.auth.domain.gateway.AuthCodeDO;
import com.github.rexsheng.springboot.faster.system.auth.domain.gateway.AuthGateway;
import com.github.rexsheng.springboot.faster.system.menu.application.dto.MenuDetailResponse;
import com.github.rexsheng.springboot.faster.system.menu.domain.SysMenuType;
import com.github.rexsheng.springboot.faster.util.DateUtil;
import com.github.rexsheng.springboot.faster.util.PasswordUtils;
import com.nimbusds.jose.JOSEException;
import jakarta.mail.MessagingException;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.www.NonceExpiredException;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@ConditionalOnClass({Authentication.class,SqlSessionFactoryBean.class})
public class AuthServiceImpl implements AuthService{
private static final Logger logger= LoggerFactory.getLogger(AuthServiceImpl.class);
private UserDomainService userDomainService;
private MenuDomainService menuDomainService;
private DeptDomainService deptDomainService;
private AuthGateway authGateway;
private TokenContainer tokenContainer;
private TokenExpireProperties tokenExpireProperties;
private KaptchaProperties kaptchaProperties;
private KaptchaProducer kaptchaProducer;
@Autowired(required = false)
private JavaMailService mailService;
public AuthServiceImpl(UserDomainService userDomainService, MenuDomainService menuDomainService, DeptDomainService deptDomainService,
AuthGateway authGateway, TokenContainer tokenContainer, TokenExpireProperties tokenExpireProperties,
KaptchaProperties kaptchaProperties, KaptchaProducer kaptchaProducer) {
this.userDomainService = userDomainService;
this.menuDomainService = menuDomainService;
this.deptDomainService = deptDomainService;
this.authGateway = authGateway;
this.tokenContainer = tokenContainer;
this.tokenExpireProperties=tokenExpireProperties;
this.kaptchaProperties=kaptchaProperties;
this.kaptchaProducer = kaptchaProducer;
}
@Override
public SysUserDetail login(LoginRequest request) {
SysUserDetail user=request.toUser();
SysUserDetail queryUserResult=userDomainService.queryByAccount(user);
if(queryUserResult==null || !queryUserResult.validatePassword(user.getPassword())){
return null;
}
loadUserAuthorities(queryUserResult);
LocalDateTime loginTime=DateUtil.currentDateTime();
userDomainService.updateLoginTime(queryUserResult.getUserId(),loginTime);
queryUserResult.setLoginTime(DateUtil.toDate(loginTime));
return queryUserResult;
}
public SysUserDetail loadUser(LoadUserRequest request) {
SysUserDetail user=userDomainService.queryById(request.getUserId());
loadUserAuthorities(user);
return user;
}
public void loadUserAuthorities(SysUserDetail user){
List allMenus=flatMap(menuDomainService.queryList());
List authCodeDOList=authGateway.queryAuthByUserId(user.getUserId());
Set authCodeSet=authCodeDOList.stream().filter(a->!ObjectUtils.isEmpty(a.getRoleCode())).map(a->"ROLE_"+a.getRoleCode()).collect(Collectors.toSet());
List filteredMenuList=authCodeDOList.stream().flatMap(auth -> flatMap(allMenus,auth.getMenuId()).stream())
.peek(a->{
if(!ObjectUtils.isEmpty(a.getMenuCode())){
authCodeSet.add(a.getMenuCode());
}
})
.filter(a-> SysMenuType.MENU.getCode().equals(a.getMenuType()))
.collect(Collectors.toMap(MenuDetailResponse::getMenuId, Function.identity(),(p1, p2)->p1)).values()
.stream()
.sorted(Comparator.comparing(MenuDetailResponse::getMenuOrder,Comparator.comparing(a->a==null?0:a)).thenComparing(MenuDetailResponse::getMenuId))
.collect(Collectors.toList());
user.setAuthorities(new ArrayList<>(authCodeSet).stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
user.setRoles(authCodeDOList.stream().filter(a->!ObjectUtils.isEmpty(a.getRoleCode())).map(a->a.getRoleCode()).distinct().collect(Collectors.toList()));
user.setMenus(buildMenuTree(filteredMenuList,null));
user.setDeptIds(deptDomainService.queryDeptAndChildrenIds(user.getDeptId()));
}
private List flatMap(List list){
List result=new ArrayList<>();
for(MenuDetailResponse item:list){
result.add(item);
if(!ObjectUtils.isEmpty(item.getChildren())){
result.addAll(flatMap(item.getChildren()));
}
}
return result;
}
private List flatMap(List allMenus,Integer leafId){
List dataList=new ArrayList<>();
Optional leafOpt=allMenus.stream().filter(a->a.getMenuId().equals(leafId)).findFirst();
if(leafOpt.isPresent()){
MenuDetailResponse leaf=leafOpt.get();
dataList.add(leaf);
if(leaf.getParentId()!=null){
dataList.addAll(flatMap(allMenus,leaf.getParentId()));
}
}
return dataList;
}
private List buildMenuTree(List list, Integer parentId){
return list.stream().filter(a->parentId==null?a.getParentId()==null:parentId.equals(a.getParentId()))
.map(a->{
SysUserDetail.SysUserMenu menu=new SysUserDetail.SysUserMenu();
menu.setMenuId(a.getMenuId());
menu.setMenuName(a.getMenuName());
menu.setMenuOrder(a.getMenuOrder());
menu.setMenuPath(a.getMenuPath());
menu.setMenuPathQuery(a.getMenuPathQuery());
menu.setParentId(a.getParentId());
menu.setMenuIcon(a.getMenuIcon());
menu.setComponent(a.getComponent());
menu.setCache(a.getCache());
menu.setVisible(a.getVisible());
menu.setFrame(a.getFrame());
menu.setFullscreen(a.getFullscreen());
menu.setTag(a.getTag());
menu.setChildren(buildMenuTree(list,a.getMenuId()));
return menu;
}).collect(Collectors.toList());
}
@Override
public Map createSessionToken(SysUserDetail user) {
return refreshSessionToken(user,null);
}
@Override
public Map refreshSessionToken(RefreshTokenRequest request) {
if(request==null){
return null;
}
final String refreshToken=request.getRefreshToken();
if(ObjectUtils.isEmpty(refreshToken)){
return null;
}
SysUserDetail user=tokenContainer.get(SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX+refreshToken);
if(user==null){
return null;
}
return refreshSessionToken(user,refreshToken);
}
private Map refreshSessionToken(SysUserDetail user,String refreshToken) {
Map result=new LinkedHashMap<>();
Map params=new LinkedHashMap<>();
params.put("u",user.getUserId());
final boolean isRefreshReq=refreshToken!=null;
if(isRefreshReq){
params.put("r",refreshToken);
}
else{
if(user.isRefreshTokenSupport()){
JwtUtil.JwtPayload jwtPayload=new JwtUtil.JwtPayload();
jwtPayload.setSubject(user.getUserId().toString());
try {
refreshToken = JwtUtil.createToken(jwtPayload, SecurityConstant.TOKEN_JWT_SECRET_KEY);
} catch (JOSEException e) {
throw new RuntimeException("Create Refresh Token Error");
}
tokenContainer.save(SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX+refreshToken,user,tokenExpireProperties.getRefreshExpires().getSeconds());
params.put("r",refreshToken);
}
}
String token= null;
try {
JwtUtil.JwtPayload jwtPayload=new JwtUtil.JwtPayload();
jwtPayload.setExpireDate(DateUtil.toDate(DateUtil.currentDateTime().plusSeconds(tokenExpireProperties.getExpires().getSeconds())));
jwtPayload.setPayload(params);
jwtPayload.setSubject(user.getAccount());
token = JwtUtil.createToken(jwtPayload, SecurityConstant.TOKEN_JWT_SECRET_KEY);
} catch (Exception e) {
throw new RuntimeException("Create Token Error");
}
tokenContainer.save(SecurityConstant.TOKEN_REDIS_KEY_PREFIX+user.getAccount()+":"+token,user,tokenExpireProperties.getExpires().getSeconds());
result.put("access_token",token);
result.put("expires_in",tokenExpireProperties.getExpires().getSeconds());
if(isRefreshReq){
if(tokenExpireProperties.getRefreshRenew()){
tokenContainer.renew(SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX+refreshToken,tokenExpireProperties.getRefreshExpires().getSeconds());
result.put("refresh_token",refreshToken);
result.put("refresh_expires_in",tokenExpireProperties.getRefreshExpires().getSeconds());
}
}
else{
result.put("refresh_token",refreshToken);
result.put("refresh_expires_in",tokenExpireProperties.getRefreshExpires().getSeconds());
}
result.put("token_type","Bearer");
return result;
}
@Override
public SysUserDetail getUserBySessionToken(String token) {
try {
JwtUtil.JwtPayload jwtPayload = JwtUtil.verifyToken(token, SecurityConstant.TOKEN_JWT_SECRET_KEY);
if(!jwtPayload.isValid()){
throw new InvalidTokenException("expire date: "+jwtPayload.getExpireDate());
}
String loginAccount = jwtPayload.getSubject();
String key=SecurityConstant.TOKEN_REDIS_KEY_PREFIX+loginAccount+":"+token;
return tokenContainer.get(key);
} catch (InvalidTokenException e) {
logger.warn("jwt已过期: {}, detail: {}", token, e.getMessage());
throw new NonceExpiredException("登录已失效,请重新登录");
} catch (Exception e) {
logger.warn("jwt解析异常",e);
throw new NonceExpiredException("登录已失效,请重新登录");
}
}
@Override
public SysUserDetail logout(String token) {
try {
JwtUtil.JwtPayload jwtPayload=JwtUtil.verifyToken(token,SecurityConstant.TOKEN_JWT_SECRET_KEY);
String loginAccount=jwtPayload.getSubject();
String refreshToken= (String) jwtPayload.getPayload().get("r");
tokenContainer.remove(SecurityConstant.TOKEN_REDIS_KEY_PREFIX+loginAccount+":"+token);
if(refreshToken!=null){
tokenContainer.remove(SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX+refreshToken);
}
SysUserDetail output=new SysUserDetail();
output.setUserId((Long) jwtPayload.getPayload().get("u"));
return output;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Map createKaptcha() throws IOException {
if(!Boolean.TRUE.equals(kaptchaProperties.getEnabled())){
return new HashMap<>();
}
Kaptcha kaptcha=kaptchaProducer.createKaptcha();
String uuid= UUID.randomUUID().toString();
Map result=new HashMap<>();
result.put("uuid",uuid);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ImageIO.write(kaptcha.getImage(), "jpg", outputStream);
// 对字节数组Base64编码
Base64.Encoder encoder = Base64.getEncoder();
result.put("img",encoder.encodeToString(outputStream.toByteArray()));
}
finally {
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
}
}
}
tokenContainer.save("kaptcha:"+uuid,kaptcha.getText(),60*3L);
return result;
}
@Override
public boolean validateKaptcha(String uuid, String code) {
if(!Boolean.TRUE.equals(kaptchaProperties.getEnabled())){
return true;
}
String value=tokenContainer.getAndDel("kaptcha:"+uuid);
if(value!=null){
return value.equalsIgnoreCase(code);
}
return false;
}
@Override
public SysUserDetail getUserByApiToken(String tokenName, String tokenValue) {
SysUserDetail queryUserResult=userDomainService.queryByApiToken(tokenName,tokenValue);
if(queryUserResult==null){
return null;
}
return queryUserResult;
}
@Override
public void offline(OfflineUserRequest request) {
List userList=userDomainService.queryList(request.getUserIds());
if(!ObjectUtils.isEmpty(userList)){
for (SysUserDetail user : userList) {
String prefix=SecurityConstant.TOKEN_REDIS_KEY_PREFIX+user.getAccount()+":";
List keys=tokenContainer.getKeysWithPrefix(prefix);
if(!ObjectUtils.isEmpty(keys)){
for (String key : keys) {
tokenContainer.remove(key);
String token=key.substring(prefix.length());
try {
JwtUtil.JwtPayload jwtPayload = JwtUtil.verifyToken(token, SecurityConstant.TOKEN_JWT_SECRET_KEY);
String refreshToken= (String) jwtPayload.getPayload().get("r");
if(refreshToken!=null){
tokenContainer.remove(SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX+refreshToken);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
prefix=SecurityConstant.REFRESH_TOKEN_REDIS_KEY_PREFIX;
List refreshTokenKeys=tokenContainer.getKeysWithPrefix(prefix);
if(!ObjectUtils.isEmpty(refreshTokenKeys)){
for (String key : refreshTokenKeys) {
String token=key.substring(prefix.length());
try {
JwtUtil.JwtPayload jwtPayload = JwtUtil.verifyToken(token, SecurityConstant.TOKEN_JWT_SECRET_KEY);
String userId=jwtPayload.getSubject();
if(userId!=null && userId.equals(String.valueOf(user.getUserId()))){
tokenContainer.remove(key);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}
@Override
public FindUserByAccountResponse validateUserAccount(ValidateAccountRequest request) {
if(validateKaptcha(request.getUuid(),request.getCaptcha())){
return findUserByAccount(request.getAccount());
}
throw new RuntimeException("验证码错误!");
}
public FindUserByAccountResponse findUserByAccount(String account){
SysUserDetail userDetail=new SysUserDetail();
userDetail.setAccount(account);
SysUserDetail user= userDomainService.queryByAccount(userDetail);
if(user!=null && user.isEnabled()){
FindUserByAccountResponse response=new FindUserByAccountResponse();
String uid= PasswordUtils.encodeRSAPrivate(String.valueOf(user.getUserId()), CommonConstant.RSA_PRIVATE_KEY_INNER);
response.setUid(uid);
response.setMail(user.getMail());
response.setPhone(user.getPhone());
return response;
}
throw new RuntimeException("用户不存在!");
}
@Override
public void sendResetPasswordMail(String uid) {
if(mailService==null){
throw new RuntimeException("邮件服务异常,请联系管理员!");
}
String userId=PasswordUtils.decodeRSAPublic(uid,CommonConstant.RSA_PUBLIC_KEY_INNER);
if(!ObjectUtils.isEmpty(userId)){
SysUserDetail user=userDomainService.queryById(Long.valueOf(userId));
if(user!=null && user.isEnabled() && !ObjectUtils.isEmpty(user.getMail())){
KaptchaProperties properties=new KaptchaProperties();
properties.setCharLength(6);
properties.setCharScope("1234567890");
String code=new DefaultTextKaptchaProducer(properties).createRandomText();
String key="ResetPasswordKaptcha:"+userId+":mail";
tokenContainer.remove(key);
tokenContainer.save(key,code,60*2L);
mailService.send(mimeMessageHelper -> {
try {
mimeMessageHelper.setSubject("密码重置通知");
mimeMessageHelper.setTo(user.getMail());
mimeMessageHelper.setText("您正在重置账号\""+user.getAccount()+"\"的密码,验证码为"+code+"(2分钟内有效)。",true);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
logger.info("找回密码已发送邮件, userId: {}, mail: {}, kaptcha: {}", userId, user.getMail(), code);
return;
}
}
throw new RuntimeException("用户或邮箱不存在!");
}
@Override
public FindUserByAccountResponse validateUserRemoteKaptcha(ValidateUserRemoteKaptchaRequest request) {
String userId=PasswordUtils.decodeRSAPublic(request.getUid(),CommonConstant.RSA_PUBLIC_KEY_INNER);
if(!ObjectUtils.isEmpty(userId)){
SysUserDetail user=userDomainService.queryById(Long.valueOf(userId));
if(user!=null && user.isEnabled()){
String key="ResetPasswordKaptcha:"+userId+":"+request.getSender();
String kaptcha=tokenContainer.get(key);
if(ObjectUtils.nullSafeEquals(kaptcha,request.getCaptcha())){
tokenContainer.remove(key);
FindUserByAccountResponse response=new FindUserByAccountResponse();
response.setAccount(PasswordUtils.encodeRSAPrivate(user.getAccount(), CommonConstant.RSA_PRIVATE_KEY_INNER));
return response;
}
throw new RuntimeException("验证码错误!");
}
}
throw new RuntimeException("用户不存在!");
}
@Override
public void findPassword(FindPasswordRequest request) {
String userId=PasswordUtils.decodeRSAPublic(request.getUid(),CommonConstant.RSA_PUBLIC_KEY_INNER);
if(!ObjectUtils.isEmpty(userId)){
SysUserDetail user=userDomainService.queryById(Long.valueOf(userId));
if(user!=null && user.isEnabled()
&& !ObjectUtils.isEmpty(user.getAccount())
&& user.getAccount().equals(PasswordUtils.decodeRSAPublic(request.getAccount(),CommonConstant.RSA_PUBLIC_KEY_INNER))){
userDomainService.changePassword(user.getUserId(),request.getPassword());
return;
}
}
throw new RuntimeException("用户不存在!");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy