- admin 模块重命名 system 模块

This commit is contained in:
sin
2019-05-03 17:20:18 +08:00
parent 2dff5d63a9
commit fb96022676
124 changed files with 25 additions and 26 deletions

52
system/system-sdk/pom.xml Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>system</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>system-sdk</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,26 @@
package cn.iocoder.mall.admin.sdk.context;
import java.util.Set;
/**
* Security 上下文
*/
public class AdminSecurityContext {
private final Integer adminId;
private final Set<Integer> roleIds;
public AdminSecurityContext(Integer adminId, Set<Integer> roleIds) {
this.adminId = adminId;
this.roleIds = roleIds;
}
public Integer getAdminId() {
return adminId;
}
public Set<Integer> getRoleIds() {
return roleIds;
}
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.mall.admin.sdk.context;
/**
* {@link AdminSecurityContext} Holder
*
* 参考 spring security 的 ThreadLocalSecurityContextHolderStrategy 类,简单实现。
*/
public class AdminSecurityContextHolder {
private static final ThreadLocal<AdminSecurityContext> securityContext = new ThreadLocal<AdminSecurityContext>();
public static void setContext(AdminSecurityContext context) {
securityContext.set(context);
}
public static AdminSecurityContext getContext() {
AdminSecurityContext ctx = securityContext.get();
// 为空时,设置一个空的进去
if (ctx == null) {
ctx = new AdminSecurityContext(null, null);
securityContext.set(ctx);
}
return ctx;
}
public static void clear() {
securityContext.remove();
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.mall.admin.sdk.dict;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author Sin
* @time 2019-04-16 20:43
*/
@Data
@Accessors(chain = true)
public class Bean {
@DictVal(dicKey = "gender", dicValue = "1")
private String gender;
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.admin.sdk.dict;
import java.lang.annotation.*;
/**
* 字典转换
*
* @author Sin
* @time 2019-04-16 20:22
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictVal {
/**
* 字典的 key
*
* @return
*/
String dicKey();
/**
* 字典 value
*
* @return
*/
String dicValue();
/**
* - 暂时只有 dictDisplayName 字典值转换为 dictDisplayName 给用户界面
*
* @return
*/
String mode() default "dictDisplayName";
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.admin.sdk.dict;
import org.springframework.util.ReflectionUtils;
/**
* {@link DictVal} 处理器
*
* @author Sin
* @time 2019-04-16 20:43
*/
public class DictValProcessor {
public static void main(String[] args) {
}
public void processor(Class<?> clazz) {
// ReflectionUtils.ann
// clazz.getFi
// ReflectionUtils
}
}

View File

@@ -0,0 +1,83 @@
package cn.iocoder.mall.admin.sdk.interceptor;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.mall.admin.api.AdminAccessLogService;
import cn.iocoder.mall.admin.api.dto.AdminAccessLogAddDTO;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/**
* 访问日志拦截器
*/
@Component
public class AdminAccessLogInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 开始时间
*/
private static final ThreadLocal<Date> START_TIME = new ThreadLocal<>();
/**
* 管理员编号
*/
private static final ThreadLocal<Integer> ADMIN_ID = new ThreadLocal<>();
@Reference(lazy = true)
@Autowired(required = false) // TODO 芋艿,初始化时,会存在 spring boot 启动时,服务无法引用的情况,先暂时这么解决。
private AdminAccessLogService adminAccessLogService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 记录当前时间
START_TIME.set(new Date());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (adminAccessLogService == null) {
throw new IllegalStateException("AdminAccessLogService 服务未引入成功");
}
AdminAccessLogAddDTO accessLog = new AdminAccessLogAddDTO();
try {
accessLog.setAdminId(ADMIN_ID.get());
if (accessLog.getAdminId() == null) {
accessLog.setAdminId(AdminAccessLogAddDTO.ADMIN_ID_NULL);
}
accessLog.setUri(request.getRequestURI()); // TODO 提升:如果想要优化,可以使用 Swagger 的 @ApiOperation 注解。
accessLog.setQueryString(HttpUtil.buildQueryString(request));
accessLog.setMethod(request.getMethod());
accessLog.setUserAgent(HttpUtil.getUserAgent(request));
accessLog.setIp(HttpUtil.getIp(request));
accessLog.setStartTime(START_TIME.get());
accessLog.setResponseTime((int) (System.currentTimeMillis() - accessLog.getStartTime().getTime()));// 默认响应时间设为0
adminAccessLogService.addAdminAccessLog(accessLog);
// TODO 提升:暂时不考虑 ELK 的方案。而是基于 MySQL 存储。如果访问日志比较多,需要定期归档。
} catch (Throwable th) {
logger.error("[afterCompletion][插入管理员访问日志({}) 发生异常({})", JSON.toJSONString(accessLog), ExceptionUtils.getRootCauseMessage(th));
} finally {
clear();
}
}
public static void setAdminId(Integer adminId) {
ADMIN_ID.set(adminId);
}
public static void clear() {
START_TIME.remove();
ADMIN_ID.remove();
}
}

View File

@@ -0,0 +1,88 @@
package cn.iocoder.mall.admin.sdk.interceptor;
import cn.iocoder.common.framework.exception.ServiceException;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.admin.api.OAuth2Service;
import cn.iocoder.mall.admin.api.bo.OAuth2AuthenticationBO;
import cn.iocoder.mall.admin.api.constant.AdminErrorCodeEnum;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContext;
import cn.iocoder.mall.admin.sdk.context.AdminSecurityContextHolder;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;
/**
* 安全拦截器
*/
@Component
public class AdminSecurityInterceptor extends HandlerInterceptorAdapter {
@Reference(validation = "true")
@Autowired(required = false) // TODO 芋艿,初始化时,会存在 spring boot 启动时,服务无法引用的情况,先暂时这么解决。
private OAuth2Service oauth2Service;
/**
* 忽略的 URL 集合,即无需经过认证
*/
private Set<String> ignoreUrls;
public AdminSecurityInterceptor setIgnoreUrls(Set<String> ignoreUrls) {
this.ignoreUrls = ignoreUrls;
return this;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 校验访问令牌是否正确。若正确,返回授权信息
String accessToken = HttpUtil.obtainAccess(request);
OAuth2AuthenticationBO authentication = null;
if (accessToken != null) {
CommonResult<OAuth2AuthenticationBO> result = oauth2Service.checkToken(accessToken);
if (result.isError()) { // TODO 芋艿,如果访问的地址无需登录,这里也不用抛异常
throw new ServiceException(result.getCode(), result.getMessage());
}
authentication = result.getData();
// 添加到 AdminSecurityContext
AdminSecurityContext context = new AdminSecurityContext(authentication.getAdminId(), authentication.getRoleIds());
AdminSecurityContextHolder.setContext(context);
// 同时也记录管理员编号到 AdminAccessLogInterceptor 中。因为:
// AdminAccessLogInterceptor 需要在 AdminSecurityInterceptor 之前执行,这样记录的访问日志才健全
// AdminSecurityInterceptor 执行后,会移除 AdminSecurityContext 信息,这就导致 AdminAccessLogInterceptor 无法获得管理员编号
// 因此,这里需要进行记录
if (authentication.getAdminId() != null) {
AdminAccessLogInterceptor.setAdminId(authentication.getAdminId());
}
} else {
String url = request.getRequestURI();
if (ignoreUrls != null && !ignoreUrls.contains(url)) { // TODO 临时写死。非登陆接口,必须已经认证身份,不允许匿名访问
throw new ServiceException(AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getCode(), AdminErrorCodeEnum.OAUTH_NOT_LOGIN.getMessage());
}
}
// 校验是否需要已授权
checkPermission(request, authentication);
// 返回成功
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 清空 SecurityContext
AdminSecurityContextHolder.clear();
}
private void checkPermission(HttpServletRequest request, OAuth2AuthenticationBO authentication) {
Integer adminId = authentication != null ? authentication.getAdminId() : null;
Set<Integer> roleIds = authentication != null ? authentication.getRoleIds() : null;
String url = request.getRequestURI();
CommonResult<Boolean> result = oauth2Service.checkPermission(adminId, roleIds, url);
if (result.isError()) {
throw new ServiceException(result.getCode(), result.getMessage());
}
}
}

View File

@@ -0,0 +1,6 @@
/**
* 提供 SDK 给其它服务,使用如下功能:
*
* 1. 通过 {@link cn.iocoder.mall.admin.sdk.interceptor.UserSecurityInterceptor} 拦截器,实现需要登陆 URL 的鉴权
*/
package cn.iocoder.mall.admin.sdk;