43 changed files with 2475 additions and 27 deletions
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
<?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>ecell-internationalize</artifactId> |
||||
<groupId>com.ecell.internationalize</groupId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<groupId>com.ecell.internationalize.auth</groupId> |
||||
<artifactId>ecell-internationalize-auth</artifactId> |
||||
|
||||
<dependencies> |
||||
|
||||
<!-- SpringCloud Alibaba Nacos --> |
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config --> |
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- SpringCloud Alibaba Sentinel --> |
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- SpringBoot Actuator --> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-actuator</artifactId> |
||||
</dependency> |
||||
|
||||
|
||||
<!-- SpringBoot Web --> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
|
||||
<!-- Swagger UI --> |
||||
<dependency> |
||||
<groupId>io.springfox</groupId> |
||||
<artifactId>springfox-swagger-ui</artifactId> |
||||
<version>${swagger.fox.version}</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.ecell.internationalize.common.swagger</groupId> |
||||
<artifactId>ecell-internationalize-swagger</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.ecell.internationalize.common.core</groupId> |
||||
<artifactId>ecell-internationalize-core</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.ecell.internationalize.common.security</groupId> |
||||
<artifactId>ecell-internationalize-security</artifactId> |
||||
<version>1.0-SNAPSHOT</version> |
||||
</dependency> |
||||
|
||||
<!--lombok依赖 --> |
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
</project> |
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
package com.ecell.internationalize.auth; |
||||
|
||||
import com.ecell.internationalize.auth.config.ExceptionAdviceConfig; |
||||
import com.ecell.internationalize.common.core.utils.SpringUtils; |
||||
import com.ecell.internationalize.common.swagger.annotation.EnableCustomSwagger2; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
||||
import org.springframework.cloud.openfeign.EnableFeignClients; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
|
||||
@EnableFeignClients |
||||
@EnableCustomSwagger2 |
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class },scanBasePackages = {"com.ecell.internationalize"}) |
||||
public class EcellAuthApplication { |
||||
public static void main(String[] args) |
||||
{ |
||||
SpringApplication.run(EcellAuthApplication.class, args); |
||||
|
||||
|
||||
System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ \n"); |
||||
} |
||||
} |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
package com.ecell.internationalize.auth.config; |
||||
|
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.SpringUtils; |
||||
import com.ecell.internationalize.common.core.utils.locale.LocaleUtil; |
||||
import com.ecell.internationalize.common.core.web.domain.AjaxResult; |
||||
import net.bytebuddy.implementation.bytecode.constant.FieldConstant; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.http.HttpRequest; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@RestControllerAdvice |
||||
public class ExceptionAdviceConfig { |
||||
private static final Logger logger = LoggerFactory.getLogger(ExceptionAdviceConfig.class); |
||||
/** |
||||
* 全局异常处理,配置国际化 |
||||
* @Author: liy |
||||
* @Date: 2022/7/8 13:53 |
||||
*/ |
||||
@ExceptionHandler(Exception.class) |
||||
public AjaxResult exceptionHandler(Exception e) { |
||||
e.printStackTrace(); |
||||
|
||||
// Locale locale = LocaleUtil.getLocale();
|
||||
//
|
||||
// MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
|
||||
// String message = messageSource.getMessage("messages.login.empty", null, locale);
|
||||
String message = LocaleUtil.getMessage("messages.login.empty"); |
||||
System.out.println("获取到的国际化语言是:=="+message); |
||||
return AjaxResult.error(message); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package com.ecell.internationalize.auth.controller; |
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/auth") |
||||
public class AuthController { |
||||
|
||||
@GetMapping("test") |
||||
public String test( ){ |
||||
int a=1; |
||||
int b= a/0; |
||||
return "111"; |
||||
} |
||||
} |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
spring: |
||||
redis: |
||||
host: 120.77.209.176 |
||||
port: 6379 |
||||
database: 2 |
||||
password: Ecell...20201001 |
||||
# 配置国际化资源文件路径 |
||||
messages: |
||||
basename: i18n/messages |
||||
encoding: UTF-8 |
||||
#设置静态资源路径,多个以逗号分隔 |
||||
web: |
||||
resources: |
||||
static-locations: classpath:static/ |
||||
mybatis: |
||||
#配置SQL映射文件路径 |
||||
mapper-locations: classpath:mapper/*.xml |
||||
# 搜索指定包别名 |
||||
typeAliasesPackage: com.yisai.auth |
||||
#驼峰命名 |
||||
configuration: |
||||
map-underscore-to-camel-case: true |
||||
#开启熔断降级 |
||||
feign: |
||||
sentinel: |
||||
enabled: true |
||||
client: |
||||
config: |
||||
default: |
||||
connectTimeout: 10000 |
||||
readTimeout: 60000 |
||||
|
||||
|
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
# Tomcat |
||||
server: |
||||
port: 9998 |
||||
|
||||
# Spring |
||||
spring: |
||||
application: |
||||
# 应用名称 |
||||
name: ecell-internationalize-auth |
||||
profiles: |
||||
# 环境配置 |
||||
active: dev |
||||
cloud: |
||||
nacos: |
||||
discovery: |
||||
# 服务注册地址 |
||||
server-addr: 127.0.0.1:8848 |
||||
config: |
||||
# 配置中心地址 |
||||
server-addr: 127.0.0.1:8848 |
||||
# 配置文件格式 |
||||
file-extension: yml |
||||
# 共享配置 |
||||
shared-configs: |
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
messages.login.empty=账号或者密码不能为空 |
||||
messages.fallback.info=服务不存在或网络端口错误 |
||||
messages.login.error=用户名不存在 |
||||
messages.account.delete=您的账号已被删除 |
||||
messages.account.stop=您的账号已被禁用 |
||||
messages.success=操作成功 |
||||
messages.error=操作失败,请联系管理员处理 |
||||
messages.error.password=输入的密码错误 |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
messages.login.empty=Account or password cannot be empty |
||||
messages.fallback.info=Service does not exist or network port error |
||||
messages.login.error=user name does not exist |
||||
messages.account.delete=Your account has been deleted |
||||
messages.account.stop=Your account has been disabled |
||||
messages.success=Operation successful |
||||
messages.error=Operation failed, please contact the administrator for handling |
||||
messages.error.password=Wrong password entered |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
messages.fallback.info=\u0421\u0435\u0440\u0432\u0438\u0441\u044B \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0442 \u0438\u043B\u0438 \u043E\u0448\u0438\u0431\u043A\u0438 \u0441\u0435\u0442\u0435\u0432\u044B\u0445 \u043F\u043E\u0440\u0442\u043E\u0432 |
||||
messages.login.error=\u0418\u043C\u0435\u043D\u0438 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u043D\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 |
||||
messages.account.stop=\u0412\u0430\u0448 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D |
||||
messages.error.password=\u0412\u0432\u0435\u0434\u0435\u043D\u0430 \u043E\u0448\u0438\u0431\u043A\u0430 \u0432 \u043A\u043E\u0434\u0435 |
||||
messages.success=\u041E\u043F\u0435\u0440\u0430\u0446\u0438\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0430. |
||||
messages.account.delete=\u0412\u0430\u0448 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0431\u044B\u043B \u0443\u0434\u0430\u043B\u0435\u043D |
||||
messages.login.empty=\u041D\u043E\u043C\u0435\u0440 \u0441\u0447\u0435\u0442\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u043E\u043B\u044C \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043F\u0443\u0441\u0442\u044B\u043C |
||||
messages.error=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u0432\u044F\u0436\u0438\u0442\u0435\u0441\u044C \u0441 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u043E\u043C |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
messages.login.empty=账号或者密码不能为空 |
||||
messages.fallback.info=服务不存在或网络端口错误 |
||||
messages.login.error=用户名不存在 |
||||
messages.account.delete=您的账号已被删除 |
||||
messages.account.stop=您的账号已被禁用 |
||||
messages.success=操作成功 |
||||
messages.error=操作失败,请联系管理员处理 |
||||
messages.error.password=输入的密码错误 |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
package com.ecell.internationalize.common.core.domain; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Data |
||||
public class LoginUser implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
/** |
||||
* 用户唯一标识 |
||||
*/ |
||||
private String token; |
||||
|
||||
/** |
||||
* 用户名id |
||||
*/ |
||||
private Long userid; |
||||
|
||||
/** |
||||
* 用户名 |
||||
*/ |
||||
private String username; |
||||
|
||||
/** |
||||
* 登录时间 |
||||
*/ |
||||
private Long loginTime; |
||||
|
||||
/** |
||||
* 过期时间 |
||||
*/ |
||||
private Long expireTime; |
||||
|
||||
/** |
||||
* 登录IP地址 |
||||
*/ |
||||
private String ipaddr; |
||||
|
||||
/** |
||||
* 权限列表 |
||||
*/ |
||||
private Set<String> permissions; |
||||
|
||||
/** |
||||
* 角色列表 |
||||
*/ |
||||
private Set<String> roles; |
||||
|
||||
/** |
||||
* 用户信息 |
||||
*/ |
||||
private SysUser sysUser; |
||||
|
||||
} |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
package com.ecell.internationalize.common.core.domain; |
||||
|
||||
import com.ecell.internationalize.common.core.annotation.Excel; |
||||
import com.ecell.internationalize.common.core.web.domain.BaseEntity; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Data |
||||
public class SysRole extends BaseEntity { |
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
/** 角色ID */ |
||||
@Excel(name = "角色序号", cellType = Excel.ColumnType.NUMERIC) |
||||
private Long roleId; |
||||
|
||||
/** 角色名称 */ |
||||
@Excel(name = "角色名称") |
||||
private String roleName; |
||||
|
||||
/** 角色权限 */ |
||||
@Excel(name = "角色权限") |
||||
private String roleKey; |
||||
|
||||
/** 角色排序 */ |
||||
@Excel(name = "角色排序") |
||||
private String roleSort; |
||||
|
||||
/** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ |
||||
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") |
||||
private String dataScope; |
||||
|
||||
/** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ |
||||
private boolean menuCheckStrictly; |
||||
|
||||
/** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ |
||||
private boolean deptCheckStrictly; |
||||
|
||||
/** 角色状态(0正常 1停用) */ |
||||
@Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") |
||||
private String status; |
||||
|
||||
/** 删除标志(0代表存在 2代表删除) */ |
||||
private String delFlag; |
||||
|
||||
/** 用户是否存在此角色标识 默认不存在 */ |
||||
private boolean flag = false; |
||||
|
||||
/** 菜单组 */ |
||||
private Long[] menuIds; |
||||
|
||||
/** 部门组(数据权限) */ |
||||
private Long[] deptIds; |
||||
|
||||
public SysRole() |
||||
{ |
||||
|
||||
} |
||||
} |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
package com.ecell.internationalize.common.core.domain; |
||||
|
||||
import com.ecell.internationalize.common.core.annotation.Excel; |
||||
import com.ecell.internationalize.common.core.annotation.Excels; |
||||
import com.ecell.internationalize.common.core.web.domain.BaseEntity; |
||||
import io.swagger.annotations.ApiModel; |
||||
import io.swagger.annotations.ApiModelProperty; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.experimental.Accessors; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Data |
||||
@EqualsAndHashCode(callSuper = false) |
||||
@Accessors(chain = true) |
||||
@ApiModel(value = "SysUser对象", description = "系统用户") |
||||
public class SysUser extends BaseEntity { |
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
/** 用户ID */ |
||||
@Excel(name = "用户序号", cellType = Excel.ColumnType.NUMERIC, prompt = "用户编号") |
||||
private Long userId; |
||||
|
||||
/** 部门ID */ |
||||
@Excel(name = "部门编号", type = Excel.Type.IMPORT) |
||||
private Long deptId; |
||||
|
||||
/** 用户账号 */ |
||||
@Excel(name = "登录名称") |
||||
private String userName; |
||||
|
||||
/** 用户昵称 */ |
||||
@Excel(name = "用户名称") |
||||
private String nickName; |
||||
|
||||
/** 用户邮箱 */ |
||||
@Excel(name = "用户邮箱") |
||||
private String email; |
||||
|
||||
/** 手机号码 */ |
||||
@Excel(name = "手机号码") |
||||
private String phonenumber; |
||||
|
||||
/** 用户性别 */ |
||||
@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") |
||||
private String sex; |
||||
|
||||
/** 用户头像 */ |
||||
private String avatar; |
||||
|
||||
/** 密码 */ |
||||
private String password; |
||||
|
||||
/** 帐号状态(0正常 1停用) */ |
||||
@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") |
||||
private String status; |
||||
|
||||
/** 删除标志(0代表存在 2代表删除) */ |
||||
private String delFlag; |
||||
|
||||
/** 最后登录IP */ |
||||
@Excel(name = "最后登录IP", type = Excel.Type.EXPORT) |
||||
private String loginIp; |
||||
|
||||
/** 最后登录时间 */ |
||||
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT) |
||||
private Date loginDate; |
||||
|
||||
/** 部门对象 */ |
||||
@Excels({ |
||||
@Excel(name = "部门名称", targetAttr = "deptName", type = Excel.Type.EXPORT), |
||||
@Excel(name = "部门负责人", targetAttr = "leader", type = Excel.Type.EXPORT) |
||||
}) |
||||
|
||||
/** 角色对象 */ |
||||
private List<SysRole> roles; |
||||
|
||||
/** 角色组 */ |
||||
private Long[] roleIds; |
||||
|
||||
/** 岗位组 */ |
||||
private Long[] postIds; |
||||
|
||||
/** 角色ID */ |
||||
private Long roleId; |
||||
|
||||
public SysUser() |
||||
{ |
||||
|
||||
} |
||||
} |
@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
||||
com.ruoyi.common.core.utils.SpringUtils |
||||
com.ecell.internationalize.common.core.utils.SpringUtils |
||||
|
||||
|
||||
|
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
import com.ecell.internationalize.common.security.config.ApplicationConfig; |
||||
import org.mybatis.spring.annotation.MapperScan; |
||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration; |
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.scheduling.annotation.EnableAsync; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Inherited |
||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(exposeProxy = true) |
||||
// 指定要扫描的Mapper类的包的路径
|
||||
@MapperScan("com.campus.**.mapper") |
||||
// 开启线程异步执行
|
||||
@EnableAsync |
||||
// 自动加载类
|
||||
@Import({ ApplicationConfig.class, FeignAutoConfiguration.class }) |
||||
public @interface EnableCustomConfig { |
||||
|
||||
} |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface InnerAuth { |
||||
/** |
||||
* 是否校验用户信息 |
||||
*/ |
||||
boolean isUser() default false; |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
|
||||
public enum Logical { |
||||
/** |
||||
* 必须具有所有的元素 |
||||
*/ |
||||
AND, |
||||
|
||||
/** |
||||
* 只需具有其中一个元素 |
||||
*/ |
||||
OR |
||||
|
||||
} |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ ElementType.METHOD, ElementType.TYPE }) |
||||
public @interface RequiresLogin { |
||||
|
||||
} |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ ElementType.METHOD, ElementType.TYPE }) |
||||
public @interface RequiresPermissions { |
||||
/** |
||||
* 需要校验的权限码 |
||||
*/ |
||||
String[] value() default {}; |
||||
|
||||
/** |
||||
* 验证模式:AND | OR,默认AND |
||||
*/ |
||||
Logical logical() default Logical.AND; |
||||
} |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package com.ecell.internationalize.common.security.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target({ ElementType.METHOD, ElementType.TYPE }) |
||||
public @interface RequiresRoles { |
||||
/** |
||||
* 需要校验的角色标识 |
||||
*/ |
||||
String[] value() default {}; |
||||
|
||||
/** |
||||
* 验证逻辑:AND | OR,默认AND |
||||
*/ |
||||
Logical logical() default Logical.AND; |
||||
} |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
package com.ecell.internationalize.common.security.aspect; |
||||
|
||||
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||
import com.ecell.internationalize.common.core.exception.InnerAuthException; |
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.security.annotation.InnerAuth; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Aspect |
||||
@Component |
||||
public class InnerAuthAspect implements Ordered { |
||||
@Around("@annotation(innerAuth)") |
||||
public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable |
||||
{ |
||||
String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE); |
||||
// 内部请求验证
|
||||
if (!StringUtils.equals(SecurityConstants.INNER, source)) |
||||
{ |
||||
throw new InnerAuthException("没有内部访问权限,不允许访问"); |
||||
} |
||||
|
||||
String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID); |
||||
String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME); |
||||
// 用户信息验证
|
||||
if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))) |
||||
{ |
||||
throw new InnerAuthException("没有设置用户信息,不允许访问 "); |
||||
} |
||||
return point.proceed(); |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return Ordered.HIGHEST_PRECEDENCE + 1; |
||||
} |
||||
} |
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
package com.ecell.internationalize.common.security.aspect; |
||||
|
||||
import com.ecell.internationalize.common.security.annotation.RequiresLogin; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresPermissions; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresRoles; |
||||
import com.ecell.internationalize.common.security.auth.AuthUtil; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Aspect |
||||
@Component |
||||
public class PreAuthorizeAspect { |
||||
/** |
||||
* 定义AOP签名 (切入所有使用鉴权注解的方法) |
||||
*/ |
||||
public static final String POINTCUT_SIGN = " @annotation(com.ecell.internationalize.common.security.annotation.RequiresLogin) || " |
||||
+ "@annotation(com.ecell.internationalize.common.security.annotation.RequiresPermissions) || " |
||||
+ "@annotation(com.ecell.internationalize.common.security.annotation.RequiresRoles)"; |
||||
|
||||
/** |
||||
* 声明AOP签名 |
||||
*/ |
||||
@Pointcut(POINTCUT_SIGN) |
||||
public void pointcut() { |
||||
} |
||||
|
||||
/** |
||||
* 环绕切入 |
||||
* |
||||
* @param joinPoint 切面对象 |
||||
* @return 底层方法执行后的返回值 |
||||
* @throws Throwable 底层方法抛出的异常 |
||||
*/ |
||||
@Around("pointcut()") |
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable |
||||
{ |
||||
// 注解鉴权
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
||||
checkMethodAnnotation(signature.getMethod()); |
||||
try |
||||
{ |
||||
// 执行原有逻辑
|
||||
Object obj = joinPoint.proceed(); |
||||
return obj; |
||||
} |
||||
catch (Throwable e) |
||||
{ |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 对一个Method对象进行注解检查 |
||||
*/ |
||||
public void checkMethodAnnotation(Method method) |
||||
{ |
||||
// 校验 @RequiresLogin 注解
|
||||
RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class); |
||||
if (requiresLogin != null) |
||||
{ |
||||
AuthUtil.checkLogin(); |
||||
} |
||||
|
||||
// 校验 @RequiresRoles 注解
|
||||
RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class); |
||||
if (requiresRoles != null) |
||||
{ |
||||
AuthUtil.checkRole(requiresRoles); |
||||
} |
||||
|
||||
// 校验 @RequiresPermissions 注解
|
||||
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class); |
||||
if (requiresPermissions != null) |
||||
{ |
||||
AuthUtil.checkPermi(requiresPermissions); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,371 @@
@@ -0,0 +1,371 @@
|
||||
package com.ecell.internationalize.common.security.auth; |
||||
|
||||
import com.ecell.internationalize.common.core.domain.LoginUser; |
||||
import com.ecell.internationalize.common.core.domain.SysUser; |
||||
import com.ecell.internationalize.common.core.exception.auth.NotLoginException; |
||||
import com.ecell.internationalize.common.core.exception.auth.NotPermissionException; |
||||
import com.ecell.internationalize.common.core.exception.auth.NotRoleException; |
||||
import com.ecell.internationalize.common.core.utils.SpringUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.security.annotation.Logical; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresLogin; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresPermissions; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresRoles; |
||||
import com.ecell.internationalize.common.security.service.TokenService; |
||||
import com.ecell.internationalize.common.security.utils.SecurityUtils; |
||||
import org.springframework.util.PatternMatchUtils; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
|
||||
public class AuthLogic { |
||||
/** 所有权限标识 */ |
||||
private static final String ALL_PERMISSION = "*:*:*"; |
||||
|
||||
/** 管理员角色权限标识 */ |
||||
private static final String SUPER_ADMIN = "admin"; |
||||
|
||||
public TokenService tokenService = SpringUtils.getBean(TokenService.class); |
||||
|
||||
/** |
||||
* 会话注销 |
||||
*/ |
||||
public void logout() |
||||
{ |
||||
String token = SecurityUtils.getToken(); |
||||
if (token == null) |
||||
{ |
||||
return; |
||||
} |
||||
logoutByToken(token); |
||||
} |
||||
|
||||
/** |
||||
* 会话注销,根据指定Token |
||||
*/ |
||||
public void logoutByToken(String token) |
||||
{ |
||||
tokenService.delLoginUser(token); |
||||
} |
||||
|
||||
/** |
||||
* 检验用户是否已经登录,如未登录,则抛出异常 |
||||
*/ |
||||
public void checkLogin() |
||||
{ |
||||
getLoginUser(); |
||||
} |
||||
|
||||
/** |
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常 |
||||
* |
||||
* @return 用户缓存信息 |
||||
*/ |
||||
public LoginUser getLoginUser() |
||||
{ |
||||
String token = SecurityUtils.getToken(); |
||||
if (token == null) |
||||
{ |
||||
throw new NotLoginException("未提供token"); |
||||
} |
||||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
||||
if (loginUser == null) |
||||
{ |
||||
throw new NotLoginException("无效的token"); |
||||
} |
||||
return loginUser; |
||||
} |
||||
|
||||
/** |
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常 |
||||
* |
||||
* @param token 前端传递的认证信息 |
||||
* @return 用户缓存信息 |
||||
*/ |
||||
public LoginUser getLoginUser(String token) |
||||
{ |
||||
return tokenService.getLoginUser(token); |
||||
} |
||||
|
||||
/** |
||||
* 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存 |
||||
* |
||||
* @param loginUser 当前用户信息 |
||||
*/ |
||||
public void verifyLoginUserExpire(LoginUser loginUser) |
||||
{ |
||||
tokenService.verifyToken(loginUser); |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否具备某权限 |
||||
* |
||||
* @param permission 权限字符串 |
||||
* @return 用户是否具备某权限 |
||||
*/ |
||||
public boolean hasPermi(String permission) |
||||
{ |
||||
return hasPermi(getPermiList(), permission); |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException |
||||
* |
||||
* @param permission 权限字符串 |
||||
* @return 用户是否具备某权限 |
||||
*/ |
||||
public void checkPermi(String permission) |
||||
{ |
||||
if (!hasPermi(getPermiList(), permission)) |
||||
{ |
||||
throw new NotPermissionException(permission); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException |
||||
* |
||||
* @param requiresPermissions 注解对象 |
||||
*/ |
||||
public void checkPermi(RequiresPermissions requiresPermissions) |
||||
{ |
||||
if (requiresPermissions.logical() == Logical.AND) |
||||
{ |
||||
checkPermiAnd(requiresPermissions.value()); |
||||
} |
||||
else |
||||
{ |
||||
checkPermiOr(requiresPermissions.value()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否含有指定权限,必须全部拥有 |
||||
* |
||||
* @param permissions 权限列表 |
||||
*/ |
||||
public void checkPermiAnd(String... permissions) |
||||
{ |
||||
Set<String> permissionList = getPermiList(); |
||||
for (String permission : permissions) |
||||
{ |
||||
if (!hasPermi(permissionList, permission)) |
||||
{ |
||||
throw new NotPermissionException(permission); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否含有指定权限,只需包含其中一个 |
||||
* |
||||
* @param permissions 权限码数组 |
||||
*/ |
||||
public void checkPermiOr(String... permissions) |
||||
{ |
||||
Set<String> permissionList = getPermiList(); |
||||
for (String permission : permissions) |
||||
{ |
||||
if (hasPermi(permissionList, permission)) |
||||
{ |
||||
return; |
||||
} |
||||
} |
||||
if (permissions.length > 0) |
||||
{ |
||||
throw new NotPermissionException(permissions); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 判断用户是否拥有某个角色 |
||||
* |
||||
* @param role 角色标识 |
||||
* @return 用户是否具备某角色 |
||||
*/ |
||||
public boolean hasRole(String role) |
||||
{ |
||||
return hasRole(getRoleList(), role); |
||||
} |
||||
|
||||
/** |
||||
* 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException |
||||
* |
||||
* @param role 角色标识 |
||||
*/ |
||||
public void checkRole(String role) |
||||
{ |
||||
if (!hasRole(role)) |
||||
{ |
||||
throw new NotRoleException(role); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据注解(@RequiresRoles)鉴权 |
||||
* |
||||
* @param requiresRoles 注解对象 |
||||
*/ |
||||
public void checkRole(RequiresRoles requiresRoles) |
||||
{ |
||||
if (requiresRoles.logical() == Logical.AND) |
||||
{ |
||||
checkRoleAnd(requiresRoles.value()); |
||||
} |
||||
else |
||||
{ |
||||
checkRoleOr(requiresRoles.value()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否含有指定角色,必须全部拥有 |
||||
* |
||||
* @param roles 角色标识数组 |
||||
*/ |
||||
public void checkRoleAnd(String... roles) |
||||
{ |
||||
Set<String> roleList = getRoleList(); |
||||
for (String role : roles) |
||||
{ |
||||
if (!hasRole(roleList, role)) |
||||
{ |
||||
throw new NotRoleException(role); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 验证用户是否含有指定角色,只需包含其中一个 |
||||
* |
||||
* @param roles 角色标识数组 |
||||
*/ |
||||
public void checkRoleOr(String... roles) |
||||
{ |
||||
Set<String> roleList = getRoleList(); |
||||
for (String role : roles) |
||||
{ |
||||
if (hasRole(roleList, role)) |
||||
{ |
||||
return; |
||||
} |
||||
} |
||||
if (roles.length > 0) |
||||
{ |
||||
throw new NotRoleException(roles); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据注解(@RequiresLogin)鉴权 |
||||
* |
||||
* @param at 注解对象 |
||||
*/ |
||||
public void checkByAnnotation(RequiresLogin at) |
||||
{ |
||||
this.checkLogin(); |
||||
} |
||||
|
||||
/** |
||||
* 根据注解(@RequiresRoles)鉴权 |
||||
* |
||||
* @param at 注解对象 |
||||
*/ |
||||
public void checkByAnnotation(RequiresRoles at) |
||||
{ |
||||
String[] roleArray = at.value(); |
||||
if (at.logical() == Logical.AND) |
||||
{ |
||||
this.checkRoleAnd(roleArray); |
||||
} |
||||
else |
||||
{ |
||||
this.checkRoleOr(roleArray); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据注解(@RequiresPermissions)鉴权 |
||||
* |
||||
* @param at 注解对象 |
||||
*/ |
||||
public void checkByAnnotation(RequiresPermissions at) |
||||
{ |
||||
String[] permissionArray = at.value(); |
||||
if (at.logical() == Logical.AND) |
||||
{ |
||||
this.checkPermiAnd(permissionArray); |
||||
} |
||||
else |
||||
{ |
||||
this.checkPermiOr(permissionArray); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取当前账号的角色列表 |
||||
* |
||||
* @return 角色列表 |
||||
*/ |
||||
public Set<String> getRoleList() |
||||
{ |
||||
try |
||||
{ |
||||
LoginUser loginUser = getLoginUser(); |
||||
return loginUser.getRoles(); |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
return new HashSet<>(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取当前账号的权限列表 |
||||
* |
||||
* @return 权限列表 |
||||
*/ |
||||
public Set<String> getPermiList() |
||||
{ |
||||
try |
||||
{ |
||||
LoginUser loginUser = getLoginUser(); |
||||
return loginUser.getPermissions(); |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
return new HashSet<>(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 判断是否包含权限 |
||||
* |
||||
* @param authorities 权限列表 |
||||
* @param permission 权限字符串 |
||||
* @return 用户是否具备某权限 |
||||
*/ |
||||
public boolean hasPermi(Collection<String> authorities, String permission) |
||||
{ |
||||
return authorities.stream().filter(StringUtils::hasText) |
||||
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission)); |
||||
} |
||||
|
||||
/** |
||||
* 判断是否包含角色 |
||||
* |
||||
* @param roles 角色列表 |
||||
* @param role 角色 |
||||
* @return 用户是否具备某角色权限 |
||||
*/ |
||||
public boolean hasRole(Collection<String> roles, String role) |
||||
{ |
||||
return roles.stream().filter(StringUtils::hasText) |
||||
.anyMatch(x -> SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role)); |
||||
} |
||||
} |
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
package com.ecell.internationalize.common.security.auth; |
||||
|
||||
import com.ecell.internationalize.common.core.domain.LoginUser; |
||||
import com.ecell.internationalize.common.core.domain.SysUser; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresPermissions; |
||||
import com.ecell.internationalize.common.security.annotation.RequiresRoles; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
|
||||
public class AuthUtil { |
||||
/** |
||||
* 底层的 AuthLogic 对象 |
||||
*/ |
||||
public static AuthLogic authLogic = new AuthLogic(); |
||||
|
||||
/** |
||||
* 会话注销 |
||||
*/ |
||||
public static void logout() |
||||
{ |
||||
authLogic.logout(); |
||||
} |
||||
|
||||
/** |
||||
* 会话注销,根据指定Token |
||||
* |
||||
* @param tokenValue 指定token |
||||
*/ |
||||
public static void logoutByToken(String token) |
||||
{ |
||||
authLogic.logoutByToken(token); |
||||
} |
||||
|
||||
/** |
||||
* 检验当前会话是否已经登录,如未登录,则抛出异常 |
||||
*/ |
||||
public static void checkLogin() |
||||
{ |
||||
authLogic.checkLogin(); |
||||
} |
||||
|
||||
/** |
||||
* 获取当前登录用户信息 |
||||
*/ |
||||
public static LoginUser getLoginUser(String token) |
||||
{ |
||||
return authLogic.getLoginUser(token); |
||||
} |
||||
|
||||
/** |
||||
* 验证当前用户有效期 |
||||
*/ |
||||
public static void verifyLoginUserExpire(LoginUser loginUser) |
||||
{ |
||||
authLogic.verifyLoginUserExpire(loginUser); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定角色标识, 返回true或false |
||||
* |
||||
* @param role 角色标识 |
||||
* @return 是否含有指定角色标识 |
||||
*/ |
||||
public static boolean hasRole(String role) |
||||
{ |
||||
return authLogic.hasRole(role); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException |
||||
* |
||||
* @param role 角色标识 |
||||
*/ |
||||
public static void checkRole(String role) |
||||
{ |
||||
authLogic.checkRole(role); |
||||
} |
||||
|
||||
/** |
||||
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException |
||||
* |
||||
* @param requiresRoles 角色权限注解 |
||||
*/ |
||||
public static void checkRole(RequiresRoles requiresRoles) |
||||
{ |
||||
authLogic.checkRole(requiresRoles); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] |
||||
* |
||||
* @param roles 角色标识数组 |
||||
*/ |
||||
public static void checkRoleAnd(String... roles) |
||||
{ |
||||
authLogic.checkRoleAnd(roles); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] |
||||
* |
||||
* @param roles 角色标识数组 |
||||
*/ |
||||
public static void checkRoleOr(String... roles) |
||||
{ |
||||
authLogic.checkRoleOr(roles); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定权限, 返回true或false |
||||
* |
||||
* @param permission 权限码 |
||||
* @return 是否含有指定权限 |
||||
*/ |
||||
public static boolean hasPermi(String permission) |
||||
{ |
||||
return authLogic.hasPermi(permission); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException |
||||
* |
||||
* @param permission 权限码 |
||||
*/ |
||||
public static void checkPermi(String permission) |
||||
{ |
||||
authLogic.checkPermi(permission); |
||||
} |
||||
|
||||
/** |
||||
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException |
||||
* |
||||
* @param requiresPermissions 权限注解 |
||||
*/ |
||||
public static void checkPermi(RequiresPermissions requiresPermissions) |
||||
{ |
||||
authLogic.checkPermi(requiresPermissions); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过] |
||||
* |
||||
* @param permissions 权限码数组 |
||||
*/ |
||||
public static void checkPermiAnd(String... permissions) |
||||
{ |
||||
authLogic.checkPermiAnd(permissions); |
||||
} |
||||
|
||||
/** |
||||
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] |
||||
* |
||||
* @param permissions 权限码数组 |
||||
*/ |
||||
public static void checkPermiOr(String... permissions) |
||||
{ |
||||
authLogic.checkPermiOr(permissions); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package com.ecell.internationalize.common.security.config; |
||||
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; |
||||
import org.springframework.context.annotation.Bean; |
||||
|
||||
import java.util.TimeZone; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
public class ApplicationConfig { |
||||
/** |
||||
* 时区配置 |
||||
*/ |
||||
@Bean |
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() |
||||
{ |
||||
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
package com.ecell.internationalize.common.security.config; |
||||
|
||||
import com.ecell.internationalize.common.security.interceptor.HeaderInterceptor; |
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
public class WebMvcConfig implements WebMvcConfigurer { |
||||
/** 不需要拦截地址 */ |
||||
public static final String[] excludeUrls = { "/login", "/logout", "/refresh" }; |
||||
|
||||
@Override |
||||
public void addInterceptors(InterceptorRegistry registry) |
||||
{ |
||||
registry.addInterceptor(getHeaderInterceptor()) |
||||
.addPathPatterns("/**") |
||||
.excludePathPatterns(excludeUrls) |
||||
.order(-10); |
||||
} |
||||
|
||||
/** |
||||
* 自定义请求头拦截器 |
||||
*/ |
||||
public HeaderInterceptor getHeaderInterceptor() |
||||
{ |
||||
return new HeaderInterceptor(); |
||||
} |
||||
} |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
package com.ecell.internationalize.common.security.feign; |
||||
|
||||
import feign.RequestInterceptor; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Configuration |
||||
public class FeignAutoConfiguration { |
||||
@Bean |
||||
public RequestInterceptor requestInterceptor() |
||||
{ |
||||
return new FeignRequestInterceptor(); |
||||
} |
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
package com.ecell.internationalize.common.security.feign; |
||||
|
||||
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.core.utils.ip.IpUtils; |
||||
import feign.RequestInterceptor; |
||||
import feign.RequestTemplate; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Component |
||||
public class FeignRequestInterceptor implements RequestInterceptor { |
||||
@Override |
||||
public void apply(RequestTemplate requestTemplate) |
||||
{ |
||||
HttpServletRequest httpServletRequest = ServletUtils.getRequest(); |
||||
if (StringUtils.isNotNull(httpServletRequest)) |
||||
{ |
||||
Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest); |
||||
// 传递用户信息请求头,防止丢失
|
||||
String userId = headers.get(SecurityConstants.DETAILS_USER_ID); |
||||
if (StringUtils.isNotEmpty(userId)) |
||||
{ |
||||
requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId); |
||||
} |
||||
String userName = headers.get(SecurityConstants.DETAILS_USERNAME); |
||||
if (StringUtils.isNotEmpty(userName)) |
||||
{ |
||||
requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName); |
||||
} |
||||
String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER); |
||||
if (StringUtils.isNotEmpty(authentication)) |
||||
{ |
||||
requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication); |
||||
} |
||||
|
||||
// 配置客户端IP
|
||||
requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(ServletUtils.getRequest())); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
package com.ecell.internationalize.common.security.handler; |
||||
|
||||
import com.ecell.internationalize.common.core.constant.HttpStatus; |
||||
import com.ecell.internationalize.common.core.exception.InnerAuthException; |
||||
import com.ecell.internationalize.common.core.exception.ServiceException; |
||||
import com.ecell.internationalize.common.core.exception.auth.NotPermissionException; |
||||
import com.ecell.internationalize.common.core.exception.auth.NotRoleException; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.core.web.domain.AjaxResult; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.validation.BindException; |
||||
import org.springframework.web.HttpRequestMethodNotSupportedException; |
||||
import org.springframework.web.bind.MethodArgumentNotValidException; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@RestControllerAdvice |
||||
public class GlobalExceptionHandler { |
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); |
||||
|
||||
/** |
||||
* 权限码异常 |
||||
*/ |
||||
@ExceptionHandler(NotPermissionException.class) |
||||
public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) |
||||
{ |
||||
String requestURI = request.getRequestURI(); |
||||
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); |
||||
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
||||
} |
||||
|
||||
/** |
||||
* 角色权限异常 |
||||
*/ |
||||
@ExceptionHandler(NotRoleException.class) |
||||
public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) |
||||
{ |
||||
String requestURI = request.getRequestURI(); |
||||
log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); |
||||
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
||||
} |
||||
|
||||
/** |
||||
* 请求方式不支持 |
||||
*/ |
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
||||
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, |
||||
HttpServletRequest request) |
||||
{ |
||||
String requestURI = request.getRequestURI(); |
||||
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); |
||||
return AjaxResult.error(e.getMessage()); |
||||
} |
||||
|
||||
/** |
||||
* 业务异常 |
||||
*/ |
||||
@ExceptionHandler(ServiceException.class) |
||||
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) |
||||
{ |
||||
log.error(e.getMessage(), e); |
||||
Integer code = e.getCode(); |
||||
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); |
||||
} |
||||
|
||||
/** |
||||
* 拦截未知的运行时异常 |
||||
*/ |
||||
@ExceptionHandler(RuntimeException.class) |
||||
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) |
||||
{ |
||||
String requestURI = request.getRequestURI(); |
||||
log.error("请求地址'{}',发生未知异常.", requestURI, e); |
||||
return AjaxResult.error(e.getMessage()); |
||||
} |
||||
|
||||
/** |
||||
* 系统异常 |
||||
*/ |
||||
@ExceptionHandler(Exception.class) |
||||
public AjaxResult handleException(Exception e, HttpServletRequest request) |
||||
{ |
||||
String requestURI = request.getRequestURI(); |
||||
log.error("请求地址'{}',发生系统异常.", requestURI, e); |
||||
return AjaxResult.error(e.getMessage()); |
||||
} |
||||
|
||||
/** |
||||
* 自定义验证异常 |
||||
*/ |
||||
@ExceptionHandler(BindException.class) |
||||
public AjaxResult handleBindException(BindException e) |
||||
{ |
||||
log.error(e.getMessage(), e); |
||||
String message = e.getAllErrors().get(0).getDefaultMessage(); |
||||
return AjaxResult.error(message); |
||||
} |
||||
|
||||
/** |
||||
* 自定义验证异常 |
||||
*/ |
||||
@ExceptionHandler(MethodArgumentNotValidException.class) |
||||
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) |
||||
{ |
||||
log.error(e.getMessage(), e); |
||||
String message = e.getBindingResult().getFieldError().getDefaultMessage(); |
||||
return AjaxResult.error(message); |
||||
} |
||||
|
||||
/** |
||||
* 内部认证异常 |
||||
*/ |
||||
@ExceptionHandler(InnerAuthException.class) |
||||
public AjaxResult handleInnerAuthException(InnerAuthException e) |
||||
{ |
||||
return AjaxResult.error(e.getMessage()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
package com.ecell.internationalize.common.security.interceptor; |
||||
|
||||
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||
import com.ecell.internationalize.common.core.context.SecurityContextHolder; |
||||
import com.ecell.internationalize.common.core.domain.LoginUser; |
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.security.auth.AuthUtil; |
||||
import com.ecell.internationalize.common.security.utils.SecurityUtils; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
public class HeaderInterceptor implements AsyncHandlerInterceptor { |
||||
|
||||
@Override |
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception |
||||
{ |
||||
if (!(handler instanceof HandlerMethod)) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID)); |
||||
SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME)); |
||||
SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY)); |
||||
|
||||
String token = SecurityUtils.getToken(); |
||||
if (StringUtils.isNotEmpty(token)) |
||||
{ |
||||
LoginUser loginUser = AuthUtil.getLoginUser(token); |
||||
if (StringUtils.isNotNull(loginUser)) |
||||
{ |
||||
AuthUtil.verifyLoginUserExpire(loginUser); |
||||
SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) |
||||
throws Exception |
||||
{ |
||||
SecurityContextHolder.remove(); |
||||
} |
||||
} |
@ -0,0 +1,170 @@
@@ -0,0 +1,170 @@
|
||||
package com.ecell.internationalize.common.security.service; |
||||
|
||||
import com.alibaba.fastjson2.JSON; |
||||
import com.alibaba.fastjson2.JSONObject; |
||||
import com.ecell.internationalize.common.core.constant.CacheConstants; |
||||
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||
import com.ecell.internationalize.common.core.domain.LoginUser; |
||||
import com.ecell.internationalize.common.core.domain.SysUser; |
||||
import com.ecell.internationalize.common.core.utils.JwtUtils; |
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import com.ecell.internationalize.common.core.utils.ip.IpUtils; |
||||
import com.ecell.internationalize.common.core.utils.uuid.IdUtils; |
||||
import com.ecell.internationalize.common.redis.service.RedisService; |
||||
import com.ecell.internationalize.common.security.utils.SecurityUtils; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Component |
||||
public class TokenService { |
||||
@Autowired |
||||
private RedisService redisService; |
||||
|
||||
protected static final long MILLIS_SECOND = 1000; |
||||
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; |
||||
|
||||
private final static long expireTime = CacheConstants.EXPIRATION; |
||||
|
||||
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; |
||||
|
||||
private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE; |
||||
|
||||
/** |
||||
* 创建令牌 |
||||
*/ |
||||
public Map<String, Object> createToken(LoginUser loginUser) |
||||
{ |
||||
String token = IdUtils.fastUUID(); |
||||
Long userId = loginUser.getSysUser().getUserId(); |
||||
String userName = loginUser.getSysUser().getUserName(); |
||||
loginUser.setToken(token); |
||||
loginUser.setUserid(userId); |
||||
loginUser.setUsername(userName); |
||||
loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest())); |
||||
refreshToken(loginUser); |
||||
|
||||
// Jwt存储信息
|
||||
Map<String, Object> claimsMap = new HashMap<String, Object>(); |
||||
claimsMap.put(SecurityConstants.USER_KEY, token); |
||||
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); |
||||
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); |
||||
|
||||
// 接口返回信息
|
||||
Map<String, Object> rspMap = new HashMap<String, Object>(); |
||||
rspMap.put("access_token", JwtUtils.createToken(claimsMap)); |
||||
rspMap.put("expires_in", expireTime); |
||||
return rspMap; |
||||
} |
||||
|
||||
/** |
||||
* 获取用户身份信息 |
||||
* |
||||
* @return 用户信息 |
||||
*/ |
||||
public LoginUser getLoginUser() |
||||
{ |
||||
return getLoginUser(ServletUtils.getRequest()); |
||||
} |
||||
|
||||
/** |
||||
* 获取用户身份信息 |
||||
* |
||||
* @return 用户信息 |
||||
*/ |
||||
public LoginUser getLoginUser(HttpServletRequest request) |
||||
{ |
||||
// 获取请求携带的令牌
|
||||
String token = SecurityUtils.getToken(request); |
||||
return getLoginUser(token); |
||||
} |
||||
|
||||
/** |
||||
* 获取用户身份信息 |
||||
* |
||||
* @return 用户信息 |
||||
*/ |
||||
public LoginUser getLoginUser(String token) |
||||
{ |
||||
LoginUser user = null; |
||||
try |
||||
{ |
||||
if (StringUtils.isNotEmpty(token)) |
||||
{ |
||||
String userkey = JwtUtils.getUserKey(token); |
||||
user = redisService.getCacheObject(getTokenKey(userkey)); |
||||
return user; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
} |
||||
return user; |
||||
} |
||||
|
||||
/** |
||||
* 设置用户身份信息 |
||||
*/ |
||||
public void setLoginUser(LoginUser loginUser) |
||||
{ |
||||
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) |
||||
{ |
||||
refreshToken(loginUser); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 删除用户缓存信息 |
||||
*/ |
||||
public void delLoginUser(String token) |
||||
{ |
||||
if (StringUtils.isNotEmpty(token)) |
||||
{ |
||||
String userkey = JwtUtils.getUserKey(token); |
||||
redisService.deleteObject(getTokenKey(userkey)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 验证令牌有效期,相差不足120分钟,自动刷新缓存 |
||||
* |
||||
* @param loginUser |
||||
*/ |
||||
public void verifyToken(LoginUser loginUser) |
||||
{ |
||||
long expireTime = loginUser.getExpireTime(); |
||||
long currentTime = System.currentTimeMillis(); |
||||
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) |
||||
{ |
||||
refreshToken(loginUser); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 刷新令牌有效期 |
||||
* |
||||
* @param loginUser 登录信息 |
||||
*/ |
||||
public void refreshToken(LoginUser loginUser) |
||||
{ |
||||
loginUser.setLoginTime(System.currentTimeMillis()); |
||||
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); |
||||
// 根据uuid将loginUser缓存
|
||||
String userKey = getTokenKey(loginUser.getToken()); |
||||
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); |
||||
} |
||||
|
||||
private String getTokenKey(String token) |
||||
{ |
||||
return ACCESS_TOKEN + token; |
||||
} |
||||
} |
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
package com.ecell.internationalize.common.security.utils; |
||||
|
||||
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||
import com.ecell.internationalize.common.core.constant.TokenConstants; |
||||
import com.ecell.internationalize.common.core.context.SecurityContextHolder; |
||||
import com.ecell.internationalize.common.core.domain.LoginUser; |
||||
import com.ecell.internationalize.common.core.domain.SysUser; |
||||
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
public class SecurityUtils { |
||||
/** |
||||
* 获取用户ID |
||||
*/ |
||||
public static Long getUserId() |
||||
{ |
||||
return SecurityContextHolder.getUserId(); |
||||
} |
||||
|
||||
/** |
||||
* yisai获取用户ID |
||||
*/ |
||||
public static String getStringUserId() |
||||
{ |
||||
return SecurityContextHolder.getStringUserId(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 获取用户名称 |
||||
*/ |
||||
public static String getUsername() |
||||
{ |
||||
return SecurityContextHolder.getUserName(); |
||||
} |
||||
|
||||
/** |
||||
* 获取用户key |
||||
*/ |
||||
public static String getUserKey() |
||||
{ |
||||
return SecurityContextHolder.getUserKey(); |
||||
} |
||||
|
||||
/** |
||||
* 获取登录用户信息 |
||||
*/ |
||||
public static LoginUser getLoginUser() |
||||
{ |
||||
return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); |
||||
} |
||||
|
||||
/** |
||||
* 获取请求token |
||||
*/ |
||||
public static String getToken() |
||||
{ |
||||
return getToken(ServletUtils.getRequest()); |
||||
} |
||||
|
||||
/** |
||||
* 根据request获取请求token |
||||
*/ |
||||
public static String getToken(HttpServletRequest request) |
||||
{ |
||||
// 从header获取token标识
|
||||
String token = request.getHeader(TokenConstants.AUTHENTICATION); |
||||
return replaceTokenPrefix(token); |
||||
} |
||||
|
||||
/** |
||||
* 裁剪token前缀 |
||||
*/ |
||||
public static String replaceTokenPrefix(String token) |
||||
{ |
||||
// 如果前端设置了令牌前缀,则裁剪掉前缀
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) |
||||
{ |
||||
token = token.replaceFirst(TokenConstants.PREFIX, ""); |
||||
} |
||||
return token; |
||||
} |
||||
|
||||
/** |
||||
* 是否为管理员 |
||||
* |
||||
* @param userId 用户ID |
||||
* @return 结果 |
||||
*/ |
||||
public static boolean isAdmin(Long userId) |
||||
{ |
||||
return userId != null && 1L == userId; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* yisai是否为管理员 |
||||
* |
||||
* @param userId 用户ID |
||||
* @return 结果 |
||||
*/ |
||||
public static boolean isAdminString(String userId) |
||||
{ |
||||
return userId != null && "1".equals(userId); |
||||
} |
||||
|
||||
/** |
||||
* yisai是否为厂商 |
||||
* |
||||
* @param firmFlag |
||||
* @return 结果 |
||||
*/ |
||||
public static boolean isFirm(String firmFlag) |
||||
{ |
||||
return firmFlag != null && "1".equals(firmFlag); |
||||
} |
||||
|
||||
/** |
||||
* yisai是否为代理商 |
||||
* @param firmFlag |
||||
* @return 结果 |
||||
*/ |
||||
public static boolean isAgent(String firmFlag) |
||||
{ |
||||
return firmFlag != null && "2".equals(firmFlag); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 生成BCryptPasswordEncoder密码 |
||||
* |
||||
* @param password 密码 |
||||
* @return 加密字符串 |
||||
*/ |
||||
public static String encryptPassword(String password) |
||||
{ |
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
||||
return passwordEncoder.encode(password); |
||||
} |
||||
|
||||
/** |
||||
* 判断密码是否相同 |
||||
* |
||||
* @param rawPassword 真实密码 |
||||
* @param encodedPassword 加密后字符 |
||||
* @return 结果 |
||||
*/ |
||||
public static boolean matchesPassword(String rawPassword, String encodedPassword) |
||||
{ |
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
||||
return passwordEncoder.matches(rawPassword, encodedPassword); |
||||
} |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
package com.ecell.internationalize.common.swagger.annotation; |
||||
|
||||
import com.ecell.internationalize.common.swagger.config.SwaggerAutoConfiguration; |
||||
import org.springframework.context.annotation.Import; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Target({ ElementType.TYPE }) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@Inherited |
||||
@Import({ SwaggerAutoConfiguration.class }) |
||||
public @interface EnableCustomSwagger2 { |
||||
|
||||
} |
@ -0,0 +1,128 @@
@@ -0,0 +1,128 @@
|
||||
package com.ecell.internationalize.common.swagger.config; |
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import springfox.documentation.builders.ApiInfoBuilder; |
||||
import springfox.documentation.builders.PathSelectors; |
||||
import springfox.documentation.builders.RequestHandlerSelectors; |
||||
import springfox.documentation.service.*; |
||||
import springfox.documentation.spi.DocumentationType; |
||||
import springfox.documentation.spi.service.contexts.SecurityContext; |
||||
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; |
||||
import springfox.documentation.spring.web.plugins.Docket; |
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.function.Predicate; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
|
||||
@Configuration |
||||
@EnableSwagger2 |
||||
@EnableAutoConfiguration |
||||
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true) |
||||
public class SwaggerAutoConfiguration { |
||||
/** |
||||
* 默认的排除路径,排除Spring Boot默认的错误处理路径和端点 |
||||
*/ |
||||
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**"); |
||||
|
||||
private static final String BASE_PATH = "/**"; |
||||
|
||||
@Bean |
||||
@ConditionalOnMissingBean |
||||
public SwaggerProperties swaggerProperties() |
||||
{ |
||||
return new SwaggerProperties(); |
||||
} |
||||
|
||||
@Bean |
||||
public Docket api(SwaggerProperties swaggerProperties) |
||||
{ |
||||
// base-path处理
|
||||
if (swaggerProperties.getBasePath().isEmpty()) |
||||
{ |
||||
swaggerProperties.getBasePath().add(BASE_PATH); |
||||
} |
||||
// noinspection unchecked
|
||||
List<Predicate<String>> basePath = new ArrayList<Predicate<String>>(); |
||||
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); |
||||
|
||||
// exclude-path处理
|
||||
if (swaggerProperties.getExcludePath().isEmpty()) |
||||
{ |
||||
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH); |
||||
} |
||||
|
||||
List<Predicate<String>> excludePath = new ArrayList<>(); |
||||
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); |
||||
|
||||
ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2).host(swaggerProperties.getHost()) |
||||
.apiInfo(apiInfo(swaggerProperties)).select() |
||||
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())); |
||||
|
||||
swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p))); |
||||
swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate())); |
||||
|
||||
return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/"); |
||||
} |
||||
|
||||
/** |
||||
* 安全模式,这里指定token通过Authorization头请求头传递 |
||||
*/ |
||||
private List<SecurityScheme> securitySchemes() |
||||
{ |
||||
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); |
||||
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); |
||||
return apiKeyList; |
||||
} |
||||
|
||||
/** |
||||
* 安全上下文 |
||||
*/ |
||||
private List<SecurityContext> securityContexts() |
||||
{ |
||||
List<SecurityContext> securityContexts = new ArrayList<>(); |
||||
securityContexts.add( |
||||
SecurityContext.builder() |
||||
.securityReferences(defaultAuth()) |
||||
.operationSelector(o -> o.requestMappingPattern().matches("/.*")) |
||||
.build()); |
||||
return securityContexts; |
||||
} |
||||
|
||||
/** |
||||
* 默认的全局鉴权策略 |
||||
* |
||||
* @return |
||||
*/ |
||||
private List<SecurityReference> defaultAuth() |
||||
{ |
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); |
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; |
||||
authorizationScopes[0] = authorizationScope; |
||||
List<SecurityReference> securityReferences = new ArrayList<>(); |
||||
securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); |
||||
return securityReferences; |
||||
} |
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties swaggerProperties) |
||||
{ |
||||
return new ApiInfoBuilder() |
||||
.title(swaggerProperties.getTitle()) |
||||
.description(swaggerProperties.getDescription()) |
||||
.license(swaggerProperties.getLicense()) |
||||
.licenseUrl(swaggerProperties.getLicenseUrl()) |
||||
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) |
||||
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())) |
||||
.version(swaggerProperties.getVersion()) |
||||
.build(); |
||||
} |
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
package com.ecell.internationalize.common.swagger.config; |
||||
|
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; |
||||
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; |
||||
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Component |
||||
public class SwaggerBeanPostProcessor implements BeanPostProcessor { |
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
||||
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { |
||||
customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); |
||||
} |
||||
return bean; |
||||
} |
||||
|
||||
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) { |
||||
List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null) |
||||
.collect(Collectors.toList()); |
||||
mappings.clear(); |
||||
mappings.addAll(copy); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) { |
||||
try { |
||||
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); |
||||
field.setAccessible(true); |
||||
return (List<RequestMappingInfoHandlerMapping>) field.get(bean); |
||||
} catch (IllegalArgumentException | IllegalAccessException e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,301 @@
@@ -0,0 +1,301 @@
|
||||
package com.ecell.internationalize.common.swagger.config; |
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Component |
||||
@ConfigurationProperties("swagger") |
||||
public class SwaggerProperties { |
||||
/** |
||||
* 是否开启swagger |
||||
*/ |
||||
private Boolean enabled; |
||||
|
||||
/** |
||||
* swagger会解析的包路径 |
||||
**/ |
||||
private String basePackage = ""; |
||||
|
||||
/** |
||||
* swagger会解析的url规则 |
||||
**/ |
||||
private List<String> basePath = new ArrayList<>(); |
||||
|
||||
/** |
||||
* 在basePath基础上需要排除的url规则 |
||||
**/ |
||||
private List<String> excludePath = new ArrayList<>(); |
||||
|
||||
/** |
||||
* 标题 |
||||
**/ |
||||
private String title = ""; |
||||
|
||||
/** |
||||
* 描述 |
||||
**/ |
||||
private String description = ""; |
||||
|
||||
/** |
||||
* 版本 |
||||
**/ |
||||
private String version = ""; |
||||
|
||||
/** |
||||
* 许可证 |
||||
**/ |
||||
private String license = ""; |
||||
|
||||
/** |
||||
* 许可证URL |
||||
**/ |
||||
private String licenseUrl = ""; |
||||
|
||||
/** |
||||
* 服务条款URL |
||||
**/ |
||||
private String termsOfServiceUrl = ""; |
||||
|
||||
/** |
||||
* host信息 |
||||
**/ |
||||
private String host = ""; |
||||
|
||||
/** |
||||
* 联系人信息 |
||||
*/ |
||||
private Contact contact = new Contact(); |
||||
|
||||
/** |
||||
* 全局统一鉴权配置 |
||||
**/ |
||||
private Authorization authorization = new Authorization(); |
||||
|
||||
public Boolean getEnabled() { |
||||
return enabled; |
||||
} |
||||
|
||||
public void setEnabled(Boolean enabled) { |
||||
this.enabled = enabled; |
||||
} |
||||
|
||||
public String getBasePackage() { |
||||
return basePackage; |
||||
} |
||||
|
||||
public void setBasePackage(String basePackage) { |
||||
this.basePackage = basePackage; |
||||
} |
||||
|
||||
public List<String> getBasePath() { |
||||
return basePath; |
||||
} |
||||
|
||||
public void setBasePath(List<String> basePath) { |
||||
this.basePath = basePath; |
||||
} |
||||
|
||||
public List<String> getExcludePath() { |
||||
return excludePath; |
||||
} |
||||
|
||||
public void setExcludePath(List<String> excludePath) { |
||||
this.excludePath = excludePath; |
||||
} |
||||
|
||||
public String getTitle() { |
||||
return title; |
||||
} |
||||
|
||||
public void setTitle(String title) { |
||||
this.title = title; |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return description; |
||||
} |
||||
|
||||
public void setDescription(String description) { |
||||
this.description = description; |
||||
} |
||||
|
||||
public String getVersion() { |
||||
return version; |
||||
} |
||||
|
||||
public void setVersion(String version) { |
||||
this.version = version; |
||||
} |
||||
|
||||
public String getLicense() { |
||||
return license; |
||||
} |
||||
|
||||
public void setLicense(String license) { |
||||
this.license = license; |
||||
} |
||||
|
||||
public String getLicenseUrl() { |
||||
return licenseUrl; |
||||
} |
||||
|
||||
public void setLicenseUrl(String licenseUrl) { |
||||
this.licenseUrl = licenseUrl; |
||||
} |
||||
|
||||
public String getTermsOfServiceUrl() { |
||||
return termsOfServiceUrl; |
||||
} |
||||
|
||||
public void setTermsOfServiceUrl(String termsOfServiceUrl) { |
||||
this.termsOfServiceUrl = termsOfServiceUrl; |
||||
} |
||||
|
||||
public String getHost() { |
||||
return host; |
||||
} |
||||
|
||||
public void setHost(String host) { |
||||
this.host = host; |
||||
} |
||||
|
||||
public Contact getContact() { |
||||
return contact; |
||||
} |
||||
|
||||
public void setContact(Contact contact) { |
||||
this.contact = contact; |
||||
} |
||||
|
||||
public Authorization getAuthorization() { |
||||
return authorization; |
||||
} |
||||
|
||||
public void setAuthorization(Authorization authorization) { |
||||
this.authorization = authorization; |
||||
} |
||||
|
||||
public static class Contact { |
||||
/** |
||||
* 联系人 |
||||
**/ |
||||
private String name = ""; |
||||
/** |
||||
* 联系人url |
||||
**/ |
||||
private String url = ""; |
||||
/** |
||||
* 联系人email |
||||
**/ |
||||
private String email = ""; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getUrl() { |
||||
return url; |
||||
} |
||||
|
||||
public void setUrl(String url) { |
||||
this.url = url; |
||||
} |
||||
|
||||
public String getEmail() { |
||||
return email; |
||||
} |
||||
|
||||
public void setEmail(String email) { |
||||
this.email = email; |
||||
} |
||||
} |
||||
|
||||
public static class Authorization { |
||||
/** |
||||
* 鉴权策略ID,需要和SecurityReferences ID保持一致 |
||||
*/ |
||||
private String name = ""; |
||||
|
||||
/** |
||||
* 需要开启鉴权URL的正则 |
||||
*/ |
||||
private String authRegex = "^.*$"; |
||||
|
||||
/** |
||||
* 鉴权作用域列表 |
||||
*/ |
||||
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>(); |
||||
|
||||
private List<String> tokenUrlList = new ArrayList<>(); |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getAuthRegex() { |
||||
return authRegex; |
||||
} |
||||
|
||||
public void setAuthRegex(String authRegex) { |
||||
this.authRegex = authRegex; |
||||
} |
||||
|
||||
public List<AuthorizationScope> getAuthorizationScopeList() { |
||||
return authorizationScopeList; |
||||
} |
||||
|
||||
public void setAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList) { |
||||
this.authorizationScopeList = authorizationScopeList; |
||||
} |
||||
|
||||
public List<String> getTokenUrlList() { |
||||
return tokenUrlList; |
||||
} |
||||
|
||||
public void setTokenUrlList(List<String> tokenUrlList) { |
||||
this.tokenUrlList = tokenUrlList; |
||||
} |
||||
} |
||||
|
||||
public static class AuthorizationScope { |
||||
/** |
||||
* 作用域名称 |
||||
*/ |
||||
private String scope = ""; |
||||
|
||||
/** |
||||
* 作用域描述 |
||||
*/ |
||||
private String description = ""; |
||||
|
||||
public String getScope() { |
||||
return scope; |
||||
} |
||||
|
||||
public void setScope(String scope) { |
||||
this.scope = scope; |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return description; |
||||
} |
||||
|
||||
public void setDescription(String description) { |
||||
this.description = description; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package com.ecell.internationalize.common.swagger.config; |
||||
|
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
||||
/** |
||||
* @author borui |
||||
*/ |
||||
@Configuration |
||||
public class SwaggerWebConfiguration implements WebMvcConfigurer { |
||||
@Override |
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) |
||||
{ |
||||
/** swagger-ui 地址 */ |
||||
registry.addResourceHandler("/swagger-ui/**") |
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue