1. 完成 auth 鉴权逻辑

2. 完成 admin 获取 Admin 上下文
3. 完成 user 获取 User 上下文
This commit is contained in:
YunaiV
2020-04-23 21:18:48 +08:00
parent a545d673ab
commit eb86ae7cbc
104 changed files with 815 additions and 1256 deletions

View File

@@ -1,28 +0,0 @@
package cn.iocoder.mall.admin.config;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("cn.iocoder.mall.admin.dao") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。为什么使用 proxyTargetClass 参数,参见 https://blog.csdn.net/huang_550/article/details/76492600
public class DatabaseConfiguration {
// 数据库连接池 Druid
@Bean
public ISqlInjector sqlInjector() {
return new DefaultSqlInjector(); // MyBatis Plus 逻辑删除
}
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor(); // MyBatis Plus 分页插件
}
}

View File

@@ -1,19 +0,0 @@
package cn.iocoder.mall.admin.config;
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.mall.system.api.constant.AdminErrorCodeEnum;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
@Configuration
public class ServiceExceptionConfiguration {
@EventListener(ApplicationReadyEvent.class) // 可参考 https://www.cnblogs.com/ssslinppp/p/7607509.html
public void initMessages() {
for (AdminErrorCodeEnum item : AdminErrorCodeEnum.values()) {
ServiceExceptionUtil.put(item.getCode(), item.getMessage());
}
}
}

View File

@@ -1,38 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.AdminRoleDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface AdminRoleMapper extends BaseMapper<AdminRoleDO> {
default List<AdminRoleDO> selectByAdminId( Integer adminId) {
return selectList(new QueryWrapper<AdminRoleDO>().eq("admin_id", adminId));
}
default List<AdminRoleDO> selectListByAdminIds(Collection<Integer> adminIds) {
return selectList(new QueryWrapper<AdminRoleDO>().in("admin_id", adminIds));
}
default int deleteByAdminId(Integer adminId) {
return delete(new QueryWrapper<AdminRoleDO>().eq("admin_id", adminId));
}
default int deleteByRoleId(Integer roleId) {
return delete(new QueryWrapper<AdminRoleDO>().eq("role_id", roleId));
}
/**
* 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML
*
* @param adminRoleDOs 数组
*/
int insertList(@Param("adminRoleDOs") List<AdminRoleDO> adminRoleDOs);
}

View File

@@ -1,9 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.ExceptionLogDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface ExceptionLogMapper extends BaseMapper<ExceptionLogDO> {
}

View File

@@ -1,24 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.OAuth2AccessTokenDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface OAuth2AccessTokenMapper extends BaseMapper<OAuth2AccessTokenDO> {
default int updateToInvalid(Integer userId, Integer userType) {
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
.eq("user_id", userId).eq("user_type", userType)
.eq("valid", true);
return update(new OAuth2AccessTokenDO().setValid(false), query);
}
default int updateToInvalidByRefreshToken(String refreshToken) {
QueryWrapper<OAuth2AccessTokenDO> query = new QueryWrapper<OAuth2AccessTokenDO>()
.eq("refresh_token", refreshToken).eq("valid", true);
return update(new OAuth2AccessTokenDO().setValid(false), query);
}
}

View File

@@ -1,18 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.OAuth2RefreshTokenDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
default int updateToInvalid(Integer userId, Integer userType) {
QueryWrapper<OAuth2RefreshTokenDO> query = new QueryWrapper<OAuth2RefreshTokenDO>()
.eq("user_id", userId).eq("user_type", userType)
.eq("valid", true);
return update(new OAuth2RefreshTokenDO().setValid(false), query);
}
}

View File

@@ -1,36 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.common.framework.mybatis.QueryWrapperX;
import cn.iocoder.mall.admin.dataobject.ResourceDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Set;
@Repository
public interface ResourceMapper extends BaseMapper<ResourceDO> {
// TODO 芋艿,后续改造。
List<ResourceDO> selectListByTypeAndRoleIds(@Param("type") Integer type,
@Param("roleIds") Set<Integer> roleIds);
default List<ResourceDO> selectListByPermission(String permission) {
return selectList(new QueryWrapperX<ResourceDO>().like("permissions", permission));
}
default List<ResourceDO> selectListByType(Integer type) {
return selectList(new QueryWrapperX<ResourceDO>().eqIfPresent("type", type));
}
default List<ResourceDO> selectListByIds(Set<Integer> ids) {
return selectList(new QueryWrapper<ResourceDO>().in("id", ids));
}
default int selectCountByPid(Integer pid) {
return selectCount(new QueryWrapper<ResourceDO>().eq("pid", pid));
}
}

View File

@@ -1,26 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.common.framework.mybatis.QueryWrapperX;
import cn.iocoder.mall.system.api.dto.role.RolePageDTO;
import cn.iocoder.mall.admin.dataobject.RoleDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RoleMapper extends BaseMapper<RoleDO> {
default List<RoleDO> selectList() {
return selectList(new QueryWrapper<>());
}
default IPage<RoleDO> selectPage(RolePageDTO rolePageDTO) {
return selectPage(new Page<>(rolePageDTO.getPageNo(), rolePageDTO.getPageSize()),
new QueryWrapperX<RoleDO>().likeIfPresent("name", rolePageDTO.getName()));
}
}

View File

@@ -1,38 +0,0 @@
package cn.iocoder.mall.admin.dao;
import cn.iocoder.mall.admin.dataobject.RoleResourceDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface RoleResourceMapper extends BaseMapper<RoleResourceDO> {
/**
* 批量插入。因为 MyBaits Plus 的批量插入是基于 Service 实现,所以只好写 XML
*
* @param roleResources 数组
*/
int insertList(@Param("roleResources") List<RoleResourceDO> roleResources);
default List<RoleResourceDO> selectListByResourceId(Integer resourceId) {
return selectList(new QueryWrapper<RoleResourceDO>().eq("resource_id", resourceId));
}
default List<RoleResourceDO> selectListByResourceId(Collection<Integer> resourceIds) {
return selectList(new QueryWrapper<RoleResourceDO>().in("resource_id", resourceIds));
}
default int deleteByResourceId(Integer resourceId) {
return delete(new QueryWrapper<RoleResourceDO>().eq("resource_id", resourceId));
}
default int deleteByRoleId(Integer roleId) {
return delete(new QueryWrapper<RoleResourceDO>().eq("role_id", roleId));
}
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* {@link AdminDO} 和 {@link RoleDO} 的关联表
*/
@TableName("admin_role")
@Data
@Accessors(chain = true)
public class AdminRoleDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 管理员编号(外键:{@link AdminDO}
*/
private Integer adminId;
/**
* 角色编号(外键:{@link RoleDO}
*/
private Integer roleId;
}

View File

@@ -1,118 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO;
import cn.iocoder.mall.system.api.dto.systemlog.AccessLogAddDTO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 异常日志 DO
*/
@Data
@Accessors(chain = true)
@TableName("exception_log")
public class ExceptionLogDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 链路追踪编号
*
* 一般来说通过链路追踪编号可以将访问日志错误日志链路追踪日志logger 打印日志等,结合在一起,从而进行排错。
*/
private String traceId;
/**
* 用户编号.
*
* 当管理员为空时,该值为 {@link AccessLogAddDTO#USER_ID_NULL}
*/
private Integer userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 应用名
*
* 目前读取 spring.application.name
*/
private String applicationName;
/**
* 访问地址
*/
private String uri;
/**
* 参数
*/
private String queryString;
/**
* http 方法
*/
private String method;
/**
* userAgent
*/
private String userAgent;
/**
* ip
*/
private String ip;
/**
* 异常发生时间
*/
private Date exceptionTime;
/**
* 异常名
*
* {@link Throwable#getClass()} 的类全名
*/
private String exceptionName;
/**
* 异常导致的消息
*
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}
*/
private String exceptionMessage;
/**
* 异常导致的根消息
*
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}
*/
private String exceptionRootCauseMessage;
/**
* 异常的栈轨迹
*
* {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}
*/
private String exceptionStackTrace;
/**
* 异常发生的类全名
*
* {@link StackTraceElement#getClassName()}
*/
private String exceptionClassName;
/**
* 异常发生的类文件
*
* {@link StackTraceElement#getFileName()}
*/
private String exceptionFileName;
/**
* 异常发生的方法名
*
* {@link StackTraceElement#getMethodName()}
*/
private String exceptionMethodName;
/**
* 异常发生的方法所在行
*
* {@link StackTraceElement#getLineNumber()}
*/
private Integer exceptionLineNumber;
}

View File

@@ -1,46 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* OAUTH2 AccessToken
*/
@TableName("oauth2_access_token")
@Data
@Accessors(chain = true)
public class OAuth2AccessTokenDO extends BaseDO {
/**
* 访问令牌
*/
@TableId(type = IdType.INPUT)
private String id;
/**
* 刷新令牌
*/
private String refreshToken;
/**
* 用户编号
*/
private Integer userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 过期时间
*/
private Date expiresTime;
/**
* 是否有效
*/
private Boolean valid;
}

View File

@@ -1,44 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 刷新令牌
*
* idx_uid
*/
@TableName("oauth2_refresh_token")
@Data
@Accessors(chain = true)
public class OAuth2RefreshTokenDO extends BaseDO {
/**
* 刷新令牌
*/
@TableId(type = IdType.INPUT)
private String id;
/**
* 用户编号
*/
private Integer userId;
/**
* 用户类型
*/
private Integer userType;
/**
* 是否有效
*/
private Boolean valid;
/**
* 过期时间
*/
private Date expiresTime;
}

View File

@@ -1,56 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 资源实体
*/
@TableName(value = "resource")
@Data
@Accessors(chain = true)
public class ResourceDO extends DeletableDO {
/**
* 资源编号
*/
private Integer id;
/**
* 资源类型
*/
private Integer type;
/**
* 排序
*/
private Integer sort;
/**
* 展示名
*/
private String displayName;
/**
* 父级资源编号(外键:{@link ResourceDO#id})
*/
private Integer pid;
/**
* 操作
*
* 目前当且仅当资源类型为【菜单】时,才会生效,即 handler 配置为界面 URL ,或者前端组件名,或者前端的路由。
*/
private String handler;
/**
* 图表
*
* 目前当且仅当资源类型为【菜单】时,才会生效
*/
private String icon;
/**
* 权限标识数组,使用逗号分隔。
*
* 例如system.admin.add 。
* 推荐格式为 ${系统}.${模块}.${操作} 。
*/
private String permissions;
}

View File

@@ -1,25 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 角色实体
*/
@TableName("role")
@Data
@Accessors(chain = true)
public class RoleDO extends DeletableDO {
/**
* 角色编号
*/
private Integer id;
/**
* 角色名
*/
private String name;
}

View File

@@ -1,29 +0,0 @@
package cn.iocoder.mall.admin.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* {@link RoleDO} 和 {@link ResourceDO} 的关联表
*/
@TableName("role_resource")
@Data
@Accessors(chain = true)
public class RoleResourceDO extends DeletableDO {
/**
* 编号
*/
private Integer id;
/**
* 角色编号(外键:{@link RoleDO}
*/
private Integer roleId;
/**
* 资源编号(外键:{@link ResourceDO}
*/
private Integer resourceId;
}

View File

@@ -48,28 +48,6 @@ public class AdminServiceImpl implements AdminService {
@Autowired
private RoleServiceImpl roleService;
@Override
public AdminAuthenticationBO authentication(AdminAuthenticationDTO adminAuthenticationDTO) {
AdminDO admin = adminMapper.selectByUsername(adminAuthenticationDTO.getUsername());
// 账号不存在
if (admin == null) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_USERNAME_NOT_REGISTERED.getCode());
}
// 密码不正确
if (!encodePassword(adminAuthenticationDTO.getPassword()).equals(admin.getPassword())) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_PASSWORD_ERROR.getCode());
}
// 账号被禁用
if (CommonStatusEnum.DISABLE.getValue().equals(admin.getStatus())) {
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_IS_DISABLE.getCode());
}
// 创建 accessToken
OAuth2AccessTokenBO accessTokenBO = oauth2Service.createToken(new OAuth2CreateTokenDTO().setUserId(admin.getId())
.setUserType(UserTypeEnum.ADMIN.getValue()));
// 转换返回
return AdminConvert.INSTANCE.convert2(admin).setToken(accessTokenBO);
}
@Override
public PageResult<AdminBO> getAdminPage(AdminPageDTO adminPageDTO) {
IPage<AdminDO> page = adminMapper.selectPage(adminPageDTO);
@@ -227,29 +205,4 @@ public class AdminServiceImpl implements AdminService {
return true;
}
@Override
public AdminAuthorizationBO checkPermissions(Integer adminId, List<String> permissions) {
// 查询管理员拥有的角色关联数据
List<AdminRoleDO> adminRoleList = adminRoleMapper.selectByAdminId(adminId);
Set<Integer> adminRoleIds = CollectionUtil.convertSet(adminRoleList, AdminRoleDO::getRoleId);
// 授权校验
if (!CollectionUtil.isEmpty(permissions)) {
Map<String, List<Integer>> permissionRoleMap = roleService.getPermissionRoleMap(permissions);
for (Map.Entry<String, List<Integer>> entry : permissionRoleMap.entrySet()) {
if (!CollectionUtil.containsAny(entry.getValue(), adminRoleIds)) { // 所以有任一不满足,就验证失败,抛出异常
throw ServiceExceptionUtil.exception(AdminErrorCodeEnum.ADMIN_INVALID_PERMISSION.getCode());
}
}
}
// 获得用户
AdminDO admin = adminMapper.selectById(adminId);
// 返回成功
return new AdminAuthorizationBO().setId(adminId).setUsername(admin.getUsername())
.setRoleIds(adminRoleIds);
}
private String encodePassword(String password) {
return DigestUtils.md5DigestAsHex(password.getBytes());
}
}

View File

@@ -1,4 +0,0 @@
##################### 业务模块 #####################
## OAuth2CodeService
modules.oauth2-code-service.access-token-expire-time-millis = 2880000
modules.oauth2-code-service.refresh-token-expire-time-millis = 43200000

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.mall.admin.dao.AdminRoleMapper">
<insert id="insertList">
INSERT INTO admin_role (
admin_id, role_id, create_time, deleted
) VALUES
<foreach collection="adminRoleDOs" item="adminRole" separator=",">
(#{adminRole.adminId}, #{adminRole.roleId}, #{adminRole.createTime}, #{adminRole.deleted})
</foreach>
</insert>
</mapper>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.mall.admin.dao.ResourceMapper">
<sql id="FIELDS">
id, type, sort, display_name, icon, permissions,
create_time, pid, handler
</sql>
<select id="selectListByTypeAndRoleIds" resultType="ResourceDO">
SELECT
r.id, r.type, r.sort, r.display_name,
r.create_time, r.pid, r.handler
FROM resource r, role_resource rr
WHERE r.deleted = 0
AND rr.deleted = 0
<if test="type != null">
AND r.type = #{type}
</if>
AND rr.role_id IN
<foreach item="roleId" collection="roleIds" separator="," open="(" close=")" index="">
#{roleId}
</foreach>
AND r.id = rr.resource_id
</select>
</mapper>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.mall.admin.dao.RoleResourceMapper">
<insert id="insertList">
INSERT INTO role_resource (
resource_id, role_id, create_time, deleted
) VALUES
<foreach collection="roleResources" item="roleResource" separator=",">
(#{roleResource.resourceId}, #{roleResource.roleId}, #{roleResource.createTime}, #{roleResource.deleted})
</foreach>
</insert>
</mapper>