♻️ Refactoring code. 增强租户隔离,客户端、路由 、菜单支持租户隔离

♻️ Refactoring code. 增强租户隔离,客户端、路由 、菜单支持租户隔离
This commit is contained in:
冷冷 committed 2020-03-25 15:48:37 +08:00
1 parent 611596506d
commit f3bbb4295d
21 files changed
+101 -151

No files matched your search

@@ -20,24 +20,28 @@
package com.pig4cloud.pigx.auth.config;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
import com.pig4cloud.pigx.common.security.component.PigxWebResponseExceptionTranslator;
import com.pig4cloud.pigx.common.security.service.PigxClientDetailsService;
import com.pig4cloud.pigx.common.security.service.PigxUserDetailsService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
@@ -64,8 +68,6 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
@SneakyThrows
public void configure(ClientDetailsServiceConfigurer clients) {
PigxClientDetailsService clientDetailsService = new PigxClientDetailsService(dataSource);
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
clients.withClientDetails(clientDetailsService);
}
@@ -103,3 +105,31 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
return tokenStore;
}
}
/**
* @author lengleng
* @date 2020/03/25
* <p>
* 扩展 JdbcClientDetailsService 支持多租户
*/
class PigxClientDetailsService extends JdbcClientDetailsService {
public PigxClientDetailsService(DataSource dataSource) {
super(dataSource);
}
/**
* 重写原生方法支持redis缓存
*
* @param clientId
* @return ClientDetails
* @throws InvalidClientException
*/
@Override
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
public ClientDetails loadClientByClientId(String clientId) {
super.setSelectClientDetailsSql(String.format(SecurityConstants.DEFAULT_SELECT_STATEMENT
, TenantContextHolder.getTenantId()));
return super.loadClientByClientId(clientId);
}
}
@@ -134,15 +134,10 @@ public interface SecurityConstants {
String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS
+ " from sys_oauth_client_details";
/**
* 默认的查询语句
*/
String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
/**
* 按条件client_id 查询
*/
String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ? and del_flag = 0 and tenant_id = %s";
/**
* 资源服务器默认bean名称
@@ -1,52 +0,0 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.security.service;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import javax.sql.DataSource;
/**
* @author lengleng
* @date 2018/10/22
* <p>
* see JdbcClientDetailsService
*/
public class PigxClientDetailsService extends JdbcClientDetailsService {
public PigxClientDetailsService(DataSource dataSource) {
super(dataSource);
}
/**
* 重写原生方法支持redis缓存
*
* @param clientId
* @return
* @throws InvalidClientException
*/
@Override
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
public ClientDetails loadClientByClientId(String clientId) {
return super.loadClientByClientId(clientId);
}
}
@@ -82,10 +82,4 @@ public class SysDict extends Model<SysDict> {
@TableLogic
@ApiModelProperty(value = "删除标记,1:已删除,0:正常")
private String delFlag;
/**
* 所属租户
*/
@ApiModelProperty(value = "所属租户")
private Integer tenantId;
}
@@ -94,10 +94,4 @@ public class SysDictItem extends Model<SysDictItem> {
@TableLogic
@ApiModelProperty(value = "删除标记,1:已删除,0:正常")
private String delFlag;
/**
* 所属租户
*/
@ApiModelProperty(value = "所属租户id")
private Integer tenantId;
}
@@ -124,6 +124,4 @@ public class SysLog implements Serializable {
@ApiModelProperty(value = "删除标记,1:已删除,0:正常")
private String delFlag;
}
@@ -21,6 +21,7 @@ package com.pig4cloud.pigx.admin.api.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -43,11 +44,15 @@ import javax.validation.constraints.NotBlank;
public class SysOauthClientDetails extends Model<SysOauthClientDetails> {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "id")
private Integer id;
/**
* 客户端ID
*/
@NotBlank(message = "client_id 不能为空")
@TableId(value = "client_id", type = IdType.INPUT)
@ApiModelProperty(value = "客户端id")
private String clientId;
/**
@@ -102,4 +107,10 @@ public class SysOauthClientDetails extends Model<SysOauthClientDetails> {
*/
@ApiModelProperty(value = "是否自动放行")
private String autoapprove;
/**
* 删除标记
*/
@TableLogic
@ApiModelProperty(value = "删除标记,1:已删除,0:正常")
private String delFlag;
}
@@ -90,10 +90,4 @@ public class SysSocialDetails extends Model<SysSocialDetails> {
@TableLogic
@ApiModelProperty(value = "删除标记,1:已删除,0:正常")
private String delFlag;
/**
* 租户ID
*/
@ApiModelProperty(value = "所属租户")
private Integer tenantId;
}
@@ -61,7 +61,7 @@ public class DynamicRouteInitRunner {
Boolean result = redisTemplate.delete(CacheConstants.ROUTE_KEY);
log.info("初始化网关路由 {} ", result);
routeConfService.routes().forEach(route -> {
routeConfService.list().forEach(route -> {
RouteDefinitionVo vo = new RouteDefinitionVo();
vo.setRouteName(route.getRouteName());
vo.setId(route.getRouteId());
@@ -50,12 +50,14 @@ public class SysClientController {
/**
* 通过ID查询
*
* @param id ID
* @param clientId clientId
* @return SysOauthClientDetails
*/
@GetMapping("/{id}")
public R getById(@PathVariable Integer id) {
return R.ok(clientDetailsService.getById(id));
@GetMapping("/{clientId}")
public R getByClientId(@PathVariable String clientId) {
return R.ok(clientDetailsService.list(Wrappers
.<SysOauthClientDetails>lambdaQuery()
.eq(SysOauthClientDetails::getClientId, clientId)));
}
@@ -87,14 +89,14 @@ public class SysClientController {
/**
* 删除
*
* @param id ID
* @param clientId ID
* @return success/false
*/
@SysLog("删除终端")
@DeleteMapping("/{id}")
@DeleteMapping("/{clientId}")
@PreAuthorize("@pms.hasPermission('sys_client_del')")
public R removeById(@PathVariable String id) {
return R.ok(clientDetailsService.removeClientDetailsById(id));
public R removeById(@PathVariable String clientId) {
return R.ok(clientDetailsService.removeByClientId(clientId));
}
/**
@@ -107,6 +109,6 @@ public class SysClientController {
@PutMapping
@PreAuthorize("@pms.hasPermission('sys_client_edit')")
public R update(@Valid @RequestBody SysOauthClientDetails sysOauthClientDetails) {
return R.ok(clientDetailsService.updateClientDetailsById(sysOauthClientDetails));
return R.ok(clientDetailsService.updateClientById(sysOauthClientDetails));
}
}
@@ -84,7 +84,7 @@ public class SysDictController {
* @return 同类型字典
*/
@GetMapping("/type/{type}")
@Cacheable(value = CacheConstants.DICT_DETAILS, key = "#type", unless = "#result == null")
@Cacheable(value = CacheConstants.DICT_DETAILS, key = "#type", unless = "#result.data.size == 0")
public R getDictByType(@PathVariable String type) {
return R.ok(sysDictItemService.list(Wrappers
.<SysDictItem>query().lambda()
@@ -74,9 +74,9 @@ public interface SysMenuService extends IService<SysMenu> {
/**
* 查询菜单
*
* @param menuVOSet
* @param voSet
* @param parentId
* @return
*/
List<MenuTree> filterMenu(Set<MenuVO> menuVOSet, String type, Integer parentId);
List<MenuTree> filterMenu(Set<MenuVO> voSet, String type, Integer parentId);
}
@@ -34,16 +34,16 @@ public interface SysOauthClientDetailsService extends IService<SysOauthClientDet
/**
* 通过ID删除客户端
*
* @param id
* @param clientId
* @return
*/
Boolean removeClientDetailsById(String id);
Boolean removeByClientId(String clientId);
/**
* 根据客户端信息
*
* @param sysOauthClientDetails
* @param clientDetails
* @return
*/
Boolean updateClientDetailsById(SysOauthClientDetails sysOauthClientDetails);
Boolean updateClientById(SysOauthClientDetails clientDetails);
}
@@ -32,16 +32,6 @@ import java.util.List;
*/
public interface SysRouteConfService extends IService<SysRouteConf> {
/**
* 获取全部路由
* <p>
* RedisRouteDefinitionWriter.java
* PropertiesRouteDefinitionLocator.java
*
* @return
*/
List<SysRouteConf> routes();
/**
* 更新路由信息
*
@@ -60,7 +60,7 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
private final SysRoleMenuMapper sysRoleMenuMapper;
@Override
@Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId + '_menu'", unless = "#result == null")
@Cacheable(value = CacheConstants.MENU_DETAILS, key = "#roleId + '_menu'", unless = "#result.size() == 0")
public List<MenuVO> findMenuByRoleId(Integer roleId) {
return baseMapper.listMenusByRoleId(roleId);
}
@@ -19,6 +19,7 @@
package com.pig4cloud.pigx.admin.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.admin.api.entity.SysOauthClientDetails;
import com.pig4cloud.pigx.admin.mapper.SysOauthClientDetailsMapper;
@@ -46,8 +47,9 @@ public class SysOauthClientDetailsServiceImpl extends ServiceImpl<SysOauthClient
*/
@Override
@CacheEvict(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#id")
public Boolean removeClientDetailsById(String id) {
return this.removeById(id);
public Boolean removeByClientId(String id) {
return this.remove(Wrappers.<SysOauthClientDetails>lambdaQuery()
.eq(SysOauthClientDetails::getClientId, id));
}
/**
@@ -58,7 +60,7 @@ public class SysOauthClientDetailsServiceImpl extends ServiceImpl<SysOauthClient
*/
@Override
@CacheEvict(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientDetails.clientId")
public Boolean updateClientDetailsById(SysOauthClientDetails clientDetails) {
public Boolean updateClientById(SysOauthClientDetails clientDetails) {
return this.updateById(clientDetails);
}
}
@@ -61,19 +61,6 @@ public class SysRouteConfServiceImpl extends ServiceImpl<SysRouteConfMapper, Sys
private final ApplicationEventPublisher applicationEventPublisher;
/**
* 获取全部路由
* <p>
* RedisRouteDefinitionWriter.java
* PropertiesRouteDefinitionLocator.java
*
* @return
*/
@Override
public List<SysRouteConf> routes() {
return baseMapper.selectList(Wrappers.emptyWrapper());
}
/**
* 更新路由信息
*
@@ -38,6 +38,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -51,7 +52,9 @@ import java.util.stream.Collectors;
@AllArgsConstructor
public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant> implements SysTenantService {
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
private final SysOauthClientDetailsService clientServices;
private final SysDeptRelationService deptRelationService;
private final SysRouteConfService routeConfService;
private final SysUserRoleService userRoleService;
private final SysRoleMenuService roleMenuService;
private final SysDictItemService dictItemService;
@@ -84,10 +87,12 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
* 2. 初始化权限相关表
* - sys_user
* - sys_role
* - sys_menu
* - sys_user_role
* - sys_role_menu
* - sys_dict
* - sys_dict_item
* - sys_client_details
*
* @param sysTenant 租户实体
* @return
@@ -99,24 +104,19 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
this.save(sysTenant);
// 查询系统内置字典
List<SysDict> dictList = dictService.list(Wrappers.<SysDict>lambdaQuery()
.eq(SysDict::getSystem, DictTypeEnum.SYSTEM.getType()))
.stream().map(dict -> {
SysDict sysDict = new SysDict();
BeanUtils.copyProperties(dict, sysDict, "tenantId");
return sysDict;
}).collect(Collectors.toList());
List<SysDict> dictList = new ArrayList<>(dictService.list());
// 查询系统内置字典项目
List<Integer> dictIdList = dictList.stream().map(SysDict::getId)
.collect(Collectors.toList());
List<SysDictItem> dictItemList = dictItemService.list(Wrappers.<SysDictItem>lambdaQuery()
.in(SysDictItem::getDictId, dictIdList))
.stream().map(item -> {
SysDictItem sysDictItem = new SysDictItem();
BeanUtils.copyProperties(item, sysDictItem, "tenantId");
return sysDictItem;
}).collect(Collectors.toList());
List<SysDictItem> dictItemList = new ArrayList<>(dictItemService
.list(Wrappers.<SysDictItem>lambdaQuery()
.in(SysDictItem::getDictId, dictIdList)));
// 查询当前租户菜单
List<SysMenu> menuList = menuService.list();
// 查询客户端配置
List<SysOauthClientDetails> clientDetailsList = clientServices.list();
// 查询路由配置
List<SysRouteConf> routeConfList = routeConfService.list();
// 保证插入租户为新的租户
TenantContextHolder.setTenantId(sysTenant.getId());
Configuration config = getConfig();
@@ -139,19 +139,20 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
role.setRoleCode(config.getString("defaultRoleCode"));
role.setRoleName(config.getString("defaultRoleName"));
roleService.save(role);
// 插入新的菜单
menuService.saveBatch(menuList);
// 用户角色关系
SysUserRole userRole = new SysUserRole();
userRole.setUserId(user.getUserId());
userRole.setRoleId(role.getRoleId());
userRoleService.save(userRole);
// 查询全部菜单,构造角色菜单关系
List<SysRoleMenu> collect = menuService.list()
.stream().map(menu -> {
SysRoleMenu roleMenu = new SysRoleMenu();
roleMenu.setRoleId(role.getRoleId());
roleMenu.setMenuId(menu.getMenuId());
return roleMenu;
}).collect(Collectors.toList());
List<SysRoleMenu> collect = menuList.stream().map(menu -> {
SysRoleMenu roleMenu = new SysRoleMenu();
roleMenu.setRoleId(role.getRoleId());
roleMenu.setMenuId(menu.getMenuId());
return roleMenu;
}).collect(Collectors.toList());
roleMenuService.saveBatch(collect);
// 插入系统字典
dictService.saveBatch(dictList);
@@ -161,6 +162,11 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
.filter(item -> item.getType().equals(dict.getType()))
.peek(item -> item.setDictId(dict.getId())))
.collect(Collectors.toList());
//插入客户端
clientServices.saveBatch(clientDetailsList);
// 插入路由
routeConfService.saveBatch(routeConfList);
return dictItemService.saveBatch(itemList);
}
@@ -35,6 +35,5 @@
<result property="updateTime" column="update_time"/>
<result property="remarks" column="remarks"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>
@@ -31,6 +31,5 @@
<result property="updateTime" column="update_time"/>
<result property="remarks" column="remarks"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
</mapper>
@@ -24,7 +24,8 @@
<mapper namespace="com.pig4cloud.pigx.admin.mapper.SysOauthClientDetailsMapper">
<resultMap id="sysOauthClientDetailsMap" type="com.pig4cloud.pigx.admin.api.entity.SysOauthClientDetails">
<id property="clientId" column="client_id"/>
<id property="id" column="id"/>
<result property="clientId" column="client_id"/>
<result property="resourceIds" column="resource_ids"/>
<result property="clientSecret" column="client_secret"/>
<result property="scope" column="scope"/>