Browse Source

👈 重构安全框架底层代码支持当前用户修改

master
wangxiang 3 years ago
parent
commit
0ced0372a9
  1. 18
      kicc-common/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/KiccTenantLineHandler.java
  2. 60
      kicc-common/kicc-common-data/src/main/java/com/cloud/kicc/common/data/util/TenantContextHolder.java
  3. 25
      kicc-common/kicc-common-security/src/main/java/com/cloud/kicc/common/security/exp/KiccLocalResourceServerTokenServices.java
  4. 9
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/UserController.java
  5. 7
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/UserService.java
  6. 42
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/UserServiceImpl.java

18
kicc-common/kicc-common-data/src/main/java/com/cloud/kicc/common/data/handler/KiccTenantLineHandler.java

@ -1,12 +1,10 @@
package com.cloud.kicc.common.data.handler; package com.cloud.kicc.common.data.handler;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.cloud.kicc.common.data.entity.KiccUser; import com.cloud.kicc.common.data.entity.KiccUser;
import com.cloud.kicc.common.data.override.TenantLikeExpression; import com.cloud.kicc.common.data.override.TenantLikeExpression;
import com.cloud.kicc.common.data.properties.TenantProperties; import com.cloud.kicc.common.data.properties.TenantProperties;
import com.cloud.kicc.common.data.util.TenantContextHolder;
import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Column;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -59,7 +57,21 @@ public class KiccTenantLineHandler implements TenantLineHandler {
@Override @Override
public Expression getTenantId() { public Expression getTenantId() {
// 返回当前用户所属的多租户ID进行条件拼接 // 返回当前用户所属的多租户ID进行条件拼接
return StrUtil.isNotBlank(TenantContextHolder.getTenant()) ? new TenantLikeExpression(TenantContextHolder.getTenant()): null; return ObjectUtil.isNotEmpty(getUser()) ? new TenantLikeExpression(getUser().getTenantId()): null;
}
/**
* 获取用户
*/
protected KiccUser getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (Optional.ofNullable(authentication).isPresent()) {
Object principal = authentication.getPrincipal();
if (principal instanceof KiccUser) {
return (KiccUser) principal;
}
}
return null;
} }
} }

60
kicc-common/kicc-common-data/src/main/java/com/cloud/kicc/common/data/util/TenantContextHolder.java

@ -1,60 +0,0 @@
package com.cloud.kicc.common.data.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.cloud.kicc.common.data.entity.KiccUser;
import com.cloud.kicc.common.data.override.TenantLikeExpression;
import lombok.experimental.UtilityClass;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
/**
*<p>
* 动态设置多租户
*</p>
*
* @Author: entfrm开发团队-王翔
* @Date: 2022/5/13
*/
@UtilityClass
public class TenantContextHolder {
private final ThreadLocal<String> contextHolder = new ThreadLocal();
public void setTenant(String tenantIds) {
contextHolder.set(tenantIds);
}
public String getTenant() {
String tenantIds = ObjectUtil.isNotEmpty(getUser()) ? getUser().getTenantId() : clearTenant();
return StrUtil.isNotBlank(contextHolder.get()) ? contextHolder.get() : tenantIds;
}
/**
* 校验用户多租户是否为空,为空说明当前用户未登录,需要清除当前线程多租户数据
* 不收的销毁,会出现 ThreadLocal 内存泄漏的问题
*/
public String clearTenant() {
contextHolder.remove();
return null;
}
/**
* 获取用户
* 如果当前不存在用户,正常情况多租户拼接查询会报错
*/
private KiccUser getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (Optional.ofNullable(authentication).isPresent()) {
Object principal = authentication.getPrincipal();
if (principal instanceof KiccUser) {
return (KiccUser) principal;
}
}
return null;
}
}

25
kicc-common/kicc-common-security/src/main/java/com/cloud/kicc/common/security/exp/KiccLocalResourceServerTokenServices.java

@ -45,33 +45,14 @@ public class KiccLocalResourceServerTokenServices implements ResourceServerToken
} }
OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request(); OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
// 检测是否是属于认证的KiccUser实体用户
if (!(oAuth2Authentication.getPrincipal() instanceof KiccUser)) { if (!(oAuth2Authentication.getPrincipal() instanceof KiccUser)) {
return oAuth2Authentication; return oAuth2Authentication;
} }
String clientId = oAuth2Request.getClientId();
// 根据客户端授权类型加载身份验证,考虑多种登录方式,比如手机登录,用户密码登录
Map<String, KiccUserDetailsService> userDetailsServiceMap = SpringUtil.getBeansOfType(KiccUserDetailsService.class);
Optional<KiccUserDetailsService> optional = userDetailsServiceMap.values().stream()
.filter(service -> service.support(clientId, oAuth2Request.getGrantType()))
.max(Comparator.comparingInt(Ordered::getOrder));
if (!optional.isPresent()) {
throw new InternalAuthenticationServiceException("UserDetailsService error , not register");
}
// 根据 username 查询 spring cache 最新的值 并返回
KiccUser kiccUser = (KiccUser) oAuth2Authentication.getPrincipal(); KiccUser kiccUser = (KiccUser) oAuth2Authentication.getPrincipal();
// 每次请求前都预先加载用户名密码身份验证令牌
UserDetails userDetails; Authentication userAuthentication = new UsernamePasswordAuthenticationToken(kiccUser, "N/A", kiccUser.getAuthorities());
try {
userDetails = optional.get().loadUserByUser(kiccUser);
} catch (UsernameNotFoundException notFoundException) {
throw new UnauthorizedException(String.format("%s username not found", kiccUser.getUsername()), notFoundException);
}
// 加载用户名密码身份验证令牌
Authentication userAuthentication = new UsernamePasswordAuthenticationToken(userDetails, "N/A", userDetails.getAuthorities());
OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication); OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication);
authentication.setAuthenticated(true); authentication.setAuthenticated(true);
return authentication; return authentication;

9
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/UserController.java

@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cloud.kicc.common.core.api.R; import com.cloud.kicc.common.core.api.R;
import com.cloud.kicc.common.core.constant.AppConstants; import com.cloud.kicc.common.core.constant.AppConstants;
import com.cloud.kicc.common.data.entity.KiccUser; import com.cloud.kicc.common.data.entity.KiccUser;
import com.cloud.kicc.common.data.util.TenantContextHolder;
import com.cloud.kicc.common.log.annotation.SysLog; import com.cloud.kicc.common.log.annotation.SysLog;
import com.cloud.kicc.common.security.annotation.Inner; import com.cloud.kicc.common.security.annotation.Inner;
import com.cloud.kicc.common.security.util.SecurityUtils; import com.cloud.kicc.common.security.util.SecurityUtils;
@ -24,6 +23,7 @@ import com.cloud.kicc.system.service.UserService;
import com.pig4cloud.plugin.excel.annotation.ResponseExcel; import com.pig4cloud.plugin.excel.annotation.ResponseExcel;
import com.pig4cloud.plugin.excel.annotation.Sheet; import com.pig4cloud.plugin.excel.annotation.Sheet;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@ -206,12 +206,12 @@ public class UserController {
return userService.list(); return userService.list();
} }
@SneakyThrows
@SysLog("用户多租户更改") @SysLog("用户多租户更改")
@GetMapping("/changeTenant/{tenantIds:[\\w,]+}") @GetMapping("/changeTenant/{tenantIds:[\\w,]+}")
@PreAuthorize("@pms.hasPermission('user_edit')") @PreAuthorize("@pms.hasPermission('user_edit')")
public R changeTenant(@PathVariable String[] tenantIds) { public R changeTenant(@PathVariable String[] tenantIds) {
TenantContextHolder.setTenant(StrUtil.join(",", tenantIds)); userService.setCurrentUserTenant(tenantIds);
return R.ok(); return R.ok();
} }
@ -219,7 +219,8 @@ public class UserController {
@GetMapping("/resetTenant") @GetMapping("/resetTenant")
@PreAuthorize("@pms.hasPermission('user_edit')") @PreAuthorize("@pms.hasPermission('user_edit')")
public R resetTenant() { public R resetTenant() {
TenantContextHolder.setTenant(SecurityUtils.getUser().getTenantId()); User user = userService.getOne(Wrappers.<User>query().lambda().eq(User::getId, SecurityUtils.getUser().getId()));
userService.setCurrentUserTenant(user.getTenantId());
return R.ok(); return R.ok();
} }

7
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/UserService.java

@ -37,4 +37,11 @@ public interface UserService extends IService<User> {
*/ */
User getUserAuthority(User user); User getUserAuthority(User user);
/**
* 设置当前用户多租户信息
* @param tenantIds 多租户ids
* @return void
*/
void setCurrentUserTenant(String... tenantIds);
} }

42
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/UserServiceImpl.java

@ -8,6 +8,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.kicc.common.core.constant.SecurityConstants; import com.cloud.kicc.common.core.constant.SecurityConstants;
import com.cloud.kicc.common.core.exception.CheckedException; import com.cloud.kicc.common.core.exception.CheckedException;
import com.cloud.kicc.common.core.exception.CommonException; import com.cloud.kicc.common.core.exception.CommonException;
import com.cloud.kicc.common.data.entity.KiccUser;
import com.cloud.kicc.common.security.util.SecurityUtils;
import com.cloud.kicc.system.api.entity.Dept; import com.cloud.kicc.system.api.entity.Dept;
import com.cloud.kicc.system.api.entity.Role; import com.cloud.kicc.system.api.entity.Role;
import com.cloud.kicc.system.api.entity.User; import com.cloud.kicc.system.api.entity.User;
@ -16,8 +18,15 @@ import com.cloud.kicc.system.mapper.UserMapper;
import com.cloud.kicc.system.service.*; import com.cloud.kicc.system.service.*;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -44,6 +53,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
private final RoleService roleService; private final RoleService roleService;
private final MenuService menuService; private final MenuService menuService;
private final TenantService tenantService; private final TenantService tenantService;
private final TokenStore tokenStore;
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(); private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
@Override @Override
@ -101,6 +111,38 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return user; return user;
} }
@Override
public void setCurrentUserTenant(String... tenantIds) {
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (currentAuthentication == null) {
new CommonException("当前用户未登录,请登录后重试!");
}
OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) currentAuthentication;
OAuth2AccessToken accessToken = tokenStore.getAccessToken(oAuth2Authentication);
OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
KiccUser currentKiccUser = SecurityUtils.getUser();
// 更新当前授权成功用户的信息
KiccUser kiccUser = new KiccUser(
currentKiccUser.getId(),
currentKiccUser.getDeptId(),
currentKiccUser.getUsername(),
currentKiccUser.getPassword(),
currentKiccUser.getPhone(),
StrUtil.join(",", tenantIds),
currentKiccUser.isEnabled(),
currentKiccUser.isAccountNonExpired(),
currentKiccUser.isCredentialsNonExpired(),
currentKiccUser.isAccountNonLocked(),
currentKiccUser.getAuthorities()
);
// 加载用户名密码身份验证令牌
Authentication userAuthentication = new UsernamePasswordAuthenticationToken(kiccUser, "N/A", kiccUser.getAuthorities());
OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication);
authentication.setAuthenticated(true);
tokenStore.storeAccessToken(accessToken, authentication);
}
/** /**
* 新增用户角色信息 * 新增用户角色信息
* @param user 用户对象 * @param user 用户对象

Loading…
Cancel
Save