122
m2pool-common/common-security/common-security.iml
Normal file
122
m2pool-common/common-security/common-security.iml
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration />
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.3.19" level="project" />
|
||||
<orderEntry type="module" module-name="api-system" />
|
||||
<orderEntry type="module" module-name="common-core" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.github.openfeign:feign-core:11.8" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:11.8" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-loadbalancer:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-loadbalancer:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.projectreactor.addons:reactor-extra:3.4.8" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-cache:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.stoyanr:evictor:1.0.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba:transmittable-thread-local:2.12.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-starter:1.4.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:4.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper-spring-boot-autoconfigure:1.4.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.62" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.2.3.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.3.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.13.2.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.13.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.13.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.80" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-io:commons-io:2.11.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.zaxxer:SparseBitSet:1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:3.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-compress:1.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.6.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-mail:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.sun.mail:jakarta.mail:1.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
|
||||
<orderEntry type="module" module-name="common-redis" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.6.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.17.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.17.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.36" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.29" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.6.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.6.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.6.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.3.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.36" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.lettuce:lettuce-core:6.1.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.76.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.4.17" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-bootstrap:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.6.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:3.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.10.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.68" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.68" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
41
m2pool-common/common-security/pom.xml
Normal file
41
m2pool-common/common-security/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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>m2pool-common</artifactId>
|
||||
<groupId>com.m2pool</groupId>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-security</artifactId>
|
||||
|
||||
<description>
|
||||
安全模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- M2pool Api System -->
|
||||
<dependency>
|
||||
<groupId>com.m2pool</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- M2Pool Common Redis-->
|
||||
<dependency>
|
||||
<groupId>com.m2pool</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import com.m2pool.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.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
// 指定要扫描的Mapper类的包的路径
|
||||
@MapperScan("com.m2pool.**.com.m2pool.chat.mapper")
|
||||
// 开启线程异步执行
|
||||
@EnableAsync
|
||||
// 自动加载类
|
||||
@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
|
||||
public @interface EnableCustomConfig
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @Description 自定义feign注解
|
||||
* * 添加basePackages 主类包路径
|
||||
* @Date 2024/6/13 11:12
|
||||
* @Author dy
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@EnableFeignClients
|
||||
public @interface EnableM2PoolFeignClients {
|
||||
|
||||
String[] value() default {};
|
||||
|
||||
String[] basePackages() default { "com.m2pool" };
|
||||
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
Class<?>[] defaultConfiguration() default {};
|
||||
|
||||
Class<?>[] clients() default {};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @Description 内部认证注解
|
||||
* @Date 2024/6/13 11:16
|
||||
* @Author dy
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface InnerAuth {
|
||||
|
||||
/**
|
||||
* 是否校验用户信息
|
||||
*/
|
||||
boolean isUser() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
/**
|
||||
* @Description 权限注解验证模式设定
|
||||
* @Date 2024/6/14 9:33
|
||||
* @Author dy
|
||||
*/
|
||||
public enum Logical
|
||||
{
|
||||
/**
|
||||
* 必须具有所有的元素
|
||||
*/
|
||||
AND,
|
||||
|
||||
/**
|
||||
* 只需具有其中一个元素
|
||||
*/
|
||||
OR
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Description oepn api key权限认证:必须拥有该方法所需权限标识才能进入方法
|
||||
* @Date 2024/6/14 10:12
|
||||
* @Author dy
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface OpenApiPermissions {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 验证逻辑:AND | OR,默认AND
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Description 登录认证:登录成功后才能进入该方法
|
||||
* @Date 2024/6/14 10:11
|
||||
* @Author dy
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequiresLogin {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Description 权限认证:必须拥有该方法所需权限标识才能进入方法
|
||||
* @Date 2024/6/14 10:12
|
||||
* @Author dy
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequiresPermissions {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 验证逻辑:AND | OR,默认AND
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.m2pool.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @Description 角色认证:必须拥有该方法所需角色标识才能进入方法
|
||||
* @Date 2024/6/14 10:25
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequiresRoles {
|
||||
|
||||
/**
|
||||
* 需要校验的角色标识
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* 验证逻辑:AND | OR,默认AND
|
||||
*/
|
||||
Logical logical() default Logical.AND;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.m2pool.common.security.aspect;
|
||||
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.exception.InnerAuthException;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.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;
|
||||
|
||||
/**
|
||||
* @Description 内部服务调用验证处理
|
||||
* @Date 2024/6/13 18:08
|
||||
* @Author dy
|
||||
*/
|
||||
@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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保方法在权限认证aop执行前执行
|
||||
*/
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.m2pool.common.security.aspect;
|
||||
|
||||
import com.m2pool.common.security.annotation.OpenApiPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresLogin;
|
||||
import com.m2pool.common.security.annotation.RequiresPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresRoles;
|
||||
import com.m2pool.common.security.auth.ApiUtil;
|
||||
import com.m2pool.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;
|
||||
|
||||
/**
|
||||
* @Description 基于 Spring Aop 的注解鉴权
|
||||
* @Date 2024/6/13 18:47
|
||||
* @Author dy
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class PreAuthAspect {
|
||||
|
||||
/**
|
||||
* 构建
|
||||
*/
|
||||
public PreAuthAspect(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义AOP签名 (切入所有使用鉴权注解的方法)
|
||||
*/
|
||||
public static final String POINTCUT_SIGN = " @annotation(com.m2pool.common.security.annotation.RequiresLogin) || "
|
||||
+ "@annotation(com.m2pool.common.security.annotation.RequiresPermissions) || "
|
||||
+ "@annotation(com.m2pool.common.security.annotation.RequiresRoles) || "
|
||||
+ "@annotation(com.m2pool.common.security.annotation.OpenApiPermissions)";
|
||||
|
||||
/**
|
||||
* 声明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.checkPermission(requiresPermissions);
|
||||
}
|
||||
|
||||
//校验 @OpenApiPermissions 注解
|
||||
OpenApiPermissions openApiPermissions = method.getAnnotation(OpenApiPermissions.class);
|
||||
if(openApiPermissions != null){
|
||||
System.out.println("校验 @OpenApiPermissions 注解");
|
||||
ApiUtil.checkPermission(openApiPermissions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.m2pool.common.security.aspect;
|
||||
|
||||
import com.m2pool.common.core.annotation.RateLimiter;
|
||||
import com.m2pool.common.core.constant.HttpStatus;
|
||||
import com.m2pool.common.core.enums.LimitType;
|
||||
import com.m2pool.common.core.exception.ServiceException;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.ip.IpUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.script.RedisScript;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description 限流处理
|
||||
* @Date 2024/6/13 18:38
|
||||
* @Author dy
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class RateLimiterAspect {
|
||||
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
|
||||
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private RedisScript<Long> limitScript;
|
||||
|
||||
@Autowired
|
||||
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
|
||||
{
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setLimitScript(RedisScript<Long> limitScript)
|
||||
{
|
||||
this.limitScript = limitScript;
|
||||
}
|
||||
|
||||
@Before("@annotation(rateLimiter)")
|
||||
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
|
||||
{
|
||||
String key = rateLimiter.key();
|
||||
int time = rateLimiter.time();
|
||||
int count = rateLimiter.count();
|
||||
|
||||
String combineKey = getCombineKey(rateLimiter, point);
|
||||
List<Object> keys = Collections.singletonList(combineKey);
|
||||
try
|
||||
{
|
||||
Long number = redisTemplate.execute(limitScript, keys, count, time);
|
||||
if (StringUtils.isNull(number) || number.intValue() > count)
|
||||
{
|
||||
throw new ServiceException("访问过于频繁,请稍候再试", HttpStatus.TOO_MANY_REQUESTS);
|
||||
}
|
||||
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
|
||||
}
|
||||
catch (ServiceException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("服务器限流异常,请稍候再试");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
|
||||
{
|
||||
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
|
||||
if (rateLimiter.limitType() == LimitType.IP)
|
||||
{
|
||||
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
|
||||
}
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.m2pool.common.security.auth;
|
||||
|
||||
import com.m2pool.common.security.annotation.OpenApiPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresPermissions;
|
||||
import com.m2pool.system.api.model.OpenApiKeyInfo;
|
||||
|
||||
/**
|
||||
* @Description api 验证工具类
|
||||
* @Date 2024/6/13 19:17
|
||||
* @Author dy
|
||||
*/
|
||||
public class ApiUtil {
|
||||
|
||||
/**
|
||||
* 底层的oaReq对象
|
||||
*/
|
||||
public static OpenApiRequest oaReq = new OpenApiRequest();
|
||||
|
||||
/**
|
||||
* 检查当前apikey是否有效
|
||||
*/
|
||||
public static void checkApiKey(){
|
||||
oaReq.checkApiKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前api-key信息
|
||||
* @param apiKey
|
||||
* @return
|
||||
*/
|
||||
public static OpenApiKeyInfo getApiKeyInfo(String apiKey){return oaReq.getOpenApiKeyInfo(apiKey);}
|
||||
|
||||
/**
|
||||
* 验证当前api-key有效期
|
||||
* @param openApiKeyInfo
|
||||
*/
|
||||
public static void verifyApiKeyExpire(OpenApiKeyInfo openApiKeyInfo){
|
||||
oaReq.verifyApiKeyExpire(openApiKeyInfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断当前账号是否含有指定权限
|
||||
*
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission)
|
||||
{
|
||||
return oaReq.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission)
|
||||
{
|
||||
oaReq.checkPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param openApiPermissions 权限注解
|
||||
*/
|
||||
public static void checkPermission(OpenApiPermissions openApiPermissions)
|
||||
{
|
||||
oaReq. checkPermission(openApiPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissions)
|
||||
{
|
||||
oaReq.checkPermissionAnd(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissions)
|
||||
{
|
||||
oaReq.checkPermissionOr(permissions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
*/
|
||||
public static void logout(){oaReq.logout();}
|
||||
|
||||
/**
|
||||
* 根据token注销会话
|
||||
* @param apiKey
|
||||
*/
|
||||
public static void logoutByApiKey(String apiKey){oaReq.logoutByApiKey(apiKey);}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
package com.m2pool.common.security.auth;
|
||||
|
||||
import com.m2pool.common.core.exception.auth.NotLoginException;
|
||||
import com.m2pool.common.core.exception.auth.NotPermissionException;
|
||||
import com.m2pool.common.core.exception.auth.NotRoleException;
|
||||
import com.m2pool.common.core.utils.SpringUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.security.annotation.Logical;
|
||||
import com.m2pool.common.security.annotation.RequiresLogin;
|
||||
import com.m2pool.common.security.annotation.RequiresPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresRoles;
|
||||
import com.m2pool.common.security.service.TokenService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Description 权限验证的逻辑实现类
|
||||
* @Date 2024/6/13 19:04
|
||||
* @Author dy
|
||||
*/
|
||||
public class AuthLogic {
|
||||
|
||||
/** 所有权限标识 */
|
||||
private static final String ALL_PERMISSION = "*:*:*";
|
||||
|
||||
/** 管理员角色权限标识 */
|
||||
private static final String SUPER_ADMIN = "admin";
|
||||
|
||||
private 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@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.checkPermissionAnd(permissionArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.checkPermissionOr(permissionArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前账号的角色列表
|
||||
*
|
||||
* @return 角色列表
|
||||
*/
|
||||
public Set<String> getRoleList()
|
||||
{
|
||||
try
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
return loginUser.getRoles();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前账号的权限列表
|
||||
*
|
||||
* @return 权限列表
|
||||
*/
|
||||
public Set<String> getPermissionList()
|
||||
{
|
||||
try
|
||||
{
|
||||
LoginUser loginUser = getLoginUser();
|
||||
return loginUser.getPermissions();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否拥有某个角色
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermission(String permission)
|
||||
{
|
||||
return hasPermission(getPermissionList(), permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public void checkPermission(String permission)
|
||||
{
|
||||
System.out.println("被调用的实际是我");
|
||||
if (!hasPermission(getPermissionList(), permission))
|
||||
{
|
||||
throw new NotPermissionException(permission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param requiresPermissions 注解对象
|
||||
*/
|
||||
public void checkPermission(RequiresPermissions requiresPermissions)
|
||||
{
|
||||
if (requiresPermissions.logical() == Logical.AND)
|
||||
{
|
||||
checkPermissionAnd(requiresPermissions.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
checkPermissionOr(requiresPermissions.value());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,必须全部拥有
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
*/
|
||||
public void checkPermissionAnd(String... permissions)
|
||||
{
|
||||
Set<String> permissionList = getPermissionList();
|
||||
for (String permission : permissions)
|
||||
{
|
||||
if (!hasPermission(permissionList, permission))
|
||||
{
|
||||
throw new NotPermissionException(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,只需包含其中一个
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public void checkPermissionOr(String... permissions)
|
||||
{
|
||||
Set<String> permissionList = getPermissionList();
|
||||
for (String permission : permissions)
|
||||
{
|
||||
if (hasPermission(permissionList, permission))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (permissions.length > 0)
|
||||
{
|
||||
throw new NotPermissionException(permissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param authorities 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermission(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,149 @@
|
||||
package com.m2pool.common.security.auth;
|
||||
|
||||
import com.m2pool.common.security.annotation.RequiresPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresRoles;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
|
||||
/**
|
||||
* @Description 权限验证工具类
|
||||
* @Date 2024/6/13 19:17
|
||||
* @Author dy
|
||||
*/
|
||||
public class AuthUtil {
|
||||
|
||||
/**
|
||||
* 底层的AuthLogic对象
|
||||
*/
|
||||
public static AuthLogic authLogic = new AuthLogic();
|
||||
|
||||
/**
|
||||
* 检查当前用户是否登陆
|
||||
*/
|
||||
public static void checkLogin(){
|
||||
authLogic.checkLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登陆用户信息
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static LoginUser getLoginUser(String token){return authLogic.getLoginUser(token);}
|
||||
|
||||
/**
|
||||
* 验证当前用户有效期
|
||||
* @param loginUser
|
||||
*/
|
||||
public static void verifyLoginUserExpire(LoginUser loginUser){
|
||||
authLogic.verifyLoginUserExpire(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
*判断当前用户是否有指定的角色标识
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断当前账号是否含有指定权限
|
||||
*
|
||||
* @param permission 权限码
|
||||
* @return 是否含有指定权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission)
|
||||
{
|
||||
return authLogic.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param permission 权限码
|
||||
*/
|
||||
public static void checkPermission(String permission)
|
||||
{
|
||||
authLogic.checkPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param requiresPermissions 权限注解
|
||||
*/
|
||||
public static void checkPermission(RequiresPermissions requiresPermissions)
|
||||
{
|
||||
authLogic. checkPermission(requiresPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public static void checkPermissionAnd(String... permissions)
|
||||
{
|
||||
authLogic.checkPermissionAnd(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public static void checkPermissionOr(String... permissions)
|
||||
{
|
||||
authLogic.checkPermissionOr(permissions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
*/
|
||||
public static void logout(){authLogic.logout();}
|
||||
|
||||
/**
|
||||
* 根据token注销会话
|
||||
* @param token
|
||||
*/
|
||||
public static void logoutByToken(String token){authLogic.logoutByToken(token);}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.m2pool.common.security.auth;
|
||||
|
||||
import com.m2pool.common.core.exception.auth.NotLoginException;
|
||||
import com.m2pool.common.core.exception.auth.NotPermissionException;
|
||||
import com.m2pool.common.core.utils.SpringUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.security.annotation.Logical;
|
||||
import com.m2pool.common.security.annotation.OpenApiPermissions;
|
||||
import com.m2pool.common.security.annotation.RequiresPermissions;
|
||||
import com.m2pool.common.security.service.OpenApiService;
|
||||
import com.m2pool.common.security.utils.OpenApiUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
import com.m2pool.system.api.model.OpenApiKeyInfo;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
import javax.sound.midi.Soundbank;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Description 权限验证的逻辑实现类
|
||||
* @Date 2024/6/13 19:04
|
||||
* @Author dy
|
||||
*/
|
||||
public class OpenApiRequest {
|
||||
|
||||
/** 所有权限标识 */
|
||||
private static final String ALL_PERMISSION = "*:*:*";
|
||||
|
||||
private OpenApiService openApiService = SpringUtils.getBean(OpenApiService.class);
|
||||
|
||||
/**
|
||||
* 会话注销
|
||||
*/
|
||||
public void logout()
|
||||
{
|
||||
String apiKey = OpenApiUtils.getApiKey();
|
||||
if (apiKey == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
logoutByApiKey(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话注销,根据指定Token
|
||||
*/
|
||||
public void logoutByApiKey(String apiKey)
|
||||
{
|
||||
openApiService.delOpenApiKey(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验用户是否已经生成过apiKey,如未生成,则抛出异常
|
||||
*/
|
||||
public void checkApiKey(){getApiKey();}
|
||||
|
||||
/**
|
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常
|
||||
*
|
||||
* @return 用户缓存信息
|
||||
*/
|
||||
public OpenApiKeyInfo getApiKey()
|
||||
{
|
||||
String apiKey = OpenApiUtils.getApiKey();
|
||||
if (apiKey == null)
|
||||
{
|
||||
throw new NotLoginException("未提供API-KEY");
|
||||
}
|
||||
OpenApiKeyInfo apiKeyInfo = OpenApiUtils.getOpenApiKeyInfo();
|
||||
if (apiKeyInfo == null)
|
||||
{
|
||||
throw new NotLoginException("无效的API-KEY");
|
||||
}
|
||||
return apiKeyInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户缓存信息, 如果未登录,则抛出异常
|
||||
*
|
||||
* @param apiKey 前端传递的认证信息
|
||||
* @return 用户缓存信息
|
||||
*/
|
||||
public OpenApiKeyInfo getOpenApiKeyInfo(String apiKey)
|
||||
{
|
||||
return openApiService.getOpenApiKeyInfo(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证当前api-key有效期, 如果相差不足120分钟,自动刷新缓存
|
||||
*
|
||||
* @param openApiKey 当前用户信息
|
||||
*/
|
||||
public void verifyApiKeyExpire(OpenApiKeyInfo openApiKey)
|
||||
{
|
||||
openApiService.verifyApiKey(openApiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresPermissions)鉴权
|
||||
*
|
||||
* @param at 注解对象
|
||||
*/
|
||||
public void checkByAnnotation(RequiresPermissions at)
|
||||
{
|
||||
String[] permissionArray = at.value();
|
||||
if (at.logical() == Logical.AND)
|
||||
{
|
||||
this.checkPermissionAnd(permissionArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.checkPermissionOr(permissionArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前账号的权限列表
|
||||
*
|
||||
* @return 权限列表
|
||||
*/
|
||||
public Set<String> getPermissionList()
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenApiKeyInfo apiKeyInfo = getApiKey();
|
||||
return apiKeyInfo.getPermissions();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermission(String permission)
|
||||
{
|
||||
System.out.println("hasPermission");
|
||||
return hasPermission(getPermissionList(), permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public void checkPermission(String permission)
|
||||
{
|
||||
System.out.println("OpenApiRequest.checkPermission");
|
||||
if (!hasPermission(getPermissionList(), permission))
|
||||
{
|
||||
throw new NotPermissionException(permission);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
|
||||
*
|
||||
* @param openApiPermissions 注解对象
|
||||
*/
|
||||
public void checkPermission(OpenApiPermissions openApiPermissions)
|
||||
{
|
||||
if (openApiPermissions.logical() == Logical.AND)
|
||||
{
|
||||
checkPermissionAnd(openApiPermissions.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
checkPermissionOr(openApiPermissions.value());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,必须全部拥有
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
*/
|
||||
public void checkPermissionAnd(String... permissions)
|
||||
{
|
||||
Set<String> permissionList = getPermissionList();
|
||||
for (String permission : permissions)
|
||||
{
|
||||
if (!hasPermission(permissionList, permission))
|
||||
{
|
||||
throw new NotPermissionException(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否含有指定权限,只需包含其中一个
|
||||
*
|
||||
* @param permissions 权限码数组
|
||||
*/
|
||||
public void checkPermissionOr(String... permissions)
|
||||
{
|
||||
System.out.println("OpenApiRequest.checkPermissionOr");
|
||||
Set<String> permissionList = getPermissionList();
|
||||
for (String permission : permissions)
|
||||
{
|
||||
if (hasPermission(permissionList, permission))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (permissions.length > 0)
|
||||
{
|
||||
throw new NotPermissionException(permissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param authorities 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermission(Collection<String> authorities, String permission)
|
||||
{
|
||||
System.out.println("权限列表:"+authorities);
|
||||
System.out.println("所需权限:"+permission);
|
||||
return authorities.stream().filter(StringUtils::hasText)
|
||||
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.m2pool.common.security.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* @Description 系统配置
|
||||
* @Date 2024/6/13 10:59
|
||||
* @Author dy
|
||||
*/
|
||||
public class ApplicationConfig {
|
||||
|
||||
/**
|
||||
* 时区配置
|
||||
*/
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
|
||||
{
|
||||
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.m2pool.common.security.config;
|
||||
|
||||
import com.m2pool.common.security.interceptor.HeaderInterceptor;
|
||||
import com.m2pool.common.security.interceptor.OpenApiHeaderInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @Description 拦截器配置
|
||||
* @Date 2024/6/13 19:56
|
||||
* @Author dy
|
||||
*/
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/** token 不需要拦截的地址 */
|
||||
public static final String[] excludeUrls = {"/login","/logout","/refresh",
|
||||
"/account/hashrate_real","/account/hashrate_history","/account/hashrate_last24h",
|
||||
"/account/miners_list","/account/watch","/account/test1",
|
||||
"/miner/hashrate_real","/miner/hashrate_history","/miner/hashrate_last24h",
|
||||
"/pool/hashrate","/pool/hashrate_history","/pool/miners_list","/pool/watch","/oapi/**"};
|
||||
|
||||
public static final String[] matchUrls = {
|
||||
"/account/hashrate_real","/account/hashrate_history","/account/hashrate_last24h",
|
||||
"/account/miners_list","/account/watch","/account/test1",
|
||||
"/miner/hashrate_real","/miner/hashrate_history","/miner/hashrate_last24h",
|
||||
"/pool/hashrate","/pool/hashrate_history","/pool/miners_list","/pool/watch","/oapi/**"};
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(getHeaderInterceptor())
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(excludeUrls)
|
||||
.order(-10);
|
||||
|
||||
registry.addInterceptor(getOpenApiHeaderInterceptor())
|
||||
.addPathPatterns(matchUrls).
|
||||
excludePathPatterns("/auth/**,/system/**,/pool/**")
|
||||
.order(-15);
|
||||
}
|
||||
/**
|
||||
* 自定义请求头拦截器
|
||||
*/
|
||||
public HeaderInterceptor getHeaderInterceptor(){return new HeaderInterceptor();}
|
||||
|
||||
public OpenApiHeaderInterceptor getOpenApiHeaderInterceptor(){return new OpenApiHeaderInterceptor();}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.m2pool.common.security.feign;
|
||||
|
||||
import feign.RequestInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @Description fegin 配置注册
|
||||
* @Date 2024/6/13 20:33
|
||||
* @Author dy
|
||||
*/
|
||||
@Configuration
|
||||
public class FeignAutoConfig {
|
||||
|
||||
@Bean
|
||||
public RequestInterceptor requestInterceptor(){return new FeignRequestInterceptor();}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.m2pool.common.security.feign;
|
||||
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.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;
|
||||
|
||||
/**
|
||||
* @Description fegin 请求拦截器
|
||||
* @Date 2024/6/13 20:12
|
||||
* @Author dy
|
||||
*/
|
||||
@Component
|
||||
public class FeignRequestInterceptor implements RequestInterceptor {
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
System.out.println("feign拦截器开始执行");
|
||||
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))
|
||||
{
|
||||
template.header(SecurityConstants.DETAILS_USER_ID, userId);
|
||||
}
|
||||
String userName = headers.get(SecurityConstants.DETAILS_USERNAME);
|
||||
if (StringUtils.isNotEmpty(userName))
|
||||
{
|
||||
template.header(SecurityConstants.DETAILS_USERNAME, userName);
|
||||
}
|
||||
String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER);
|
||||
if (StringUtils.isNotEmpty(authentication))
|
||||
{
|
||||
template.header(SecurityConstants.AUTHORIZATION_HEADER, authentication);
|
||||
}
|
||||
System.out.println("userId:"+userId);
|
||||
System.out.println("userName:"+userName);
|
||||
System.out.println("ip:"+IpUtils.getIpAddr(ServletUtils.getRequest()));
|
||||
// 配置客户端IP
|
||||
template.header("X-Forwarded-For", IpUtils.getIpAddr(ServletUtils.getRequest()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package com.m2pool.common.security.handler;
|
||||
|
||||
import com.m2pool.common.core.constant.HttpStatus;
|
||||
import com.m2pool.common.core.exception.InnerAuthException;
|
||||
import com.m2pool.common.core.exception.ServiceException;
|
||||
import com.m2pool.common.core.exception.auth.NotPermissionException;
|
||||
import com.m2pool.common.core.exception.auth.NotRoleException;
|
||||
import com.m2pool.common.core.exception.file.FileNameLengthLimitExceededException;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.jdbc.BadSqlGrammarException;
|
||||
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 org.springframework.web.multipart.MultipartException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @Description 全局异常处理器
|
||||
* @Date 2024/6/13 20:36
|
||||
* @Author dy
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@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();
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
System.out.println(loginUser.getRoles());
|
||||
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(MultipartException.class)
|
||||
public AjaxResult handleMultException(MultipartException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生文件异常.", requestURI, e);
|
||||
return AjaxResult.error("文件过大,单个文件最大2M");
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截文件名超长异常
|
||||
*/
|
||||
@ExceptionHandler(FileNameLengthLimitExceededException.class)
|
||||
public AjaxResult handleFileNameLengthException(FileNameLengthLimitExceededException 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());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sql查询验证异常
|
||||
*/
|
||||
@ExceptionHandler(SQLException.class)
|
||||
public AjaxResult handleSQLException(SQLException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生sql执行异常.", requestURI, e);
|
||||
return AjaxResult.error("请求地址'{}',发生sql执行异常.");
|
||||
}
|
||||
|
||||
@ExceptionHandler(BadSqlGrammarException.class)
|
||||
public AjaxResult handleBadSqlGrammarException(BadSqlGrammarException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("请求地址'{}',发生SQL异常.", requestURI, e);
|
||||
return AjaxResult.error(HttpStatus.ERROR, "数据库异常!");
|
||||
}
|
||||
//todo 根据业务需求增加异常
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.m2pool.common.security.interceptor;
|
||||
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.context.SecurityContextHolder;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.ip.IpUtils;
|
||||
import com.m2pool.common.security.auth.AuthUtil;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Description 自定义请求头拦截器,将请求头数据封装到线程变量中方便后续获取使用
|
||||
* 同时获取当前用户有效时间并刷新有效期
|
||||
* @Date 2024/6/13 20:29
|
||||
* @Author dy
|
||||
*/
|
||||
public class HeaderInterceptor implements AsyncHandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
System.out.println("调用默认拦截器,路径:"+request.getRequestURI());
|
||||
System.out.println("调用默认拦截器,路径:"+ IpUtils.getIpAddr(request));
|
||||
System.out.println(handler);
|
||||
if(!(handler instanceof HandlerMethod)){
|
||||
System.out.println("默认拦截器放行");
|
||||
return true;
|
||||
|
||||
}
|
||||
System.out.println("默认拦截器处理");
|
||||
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,68 @@
|
||||
package com.m2pool.common.security.interceptor;
|
||||
|
||||
import com.m2pool.common.core.constant.OpenApiKeyConstants;
|
||||
import com.m2pool.common.core.context.OpenApiContextHolder;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.ip.IpUtils;
|
||||
import com.m2pool.common.security.auth.ApiUtil;
|
||||
import com.m2pool.common.security.utils.OpenApiUtils;
|
||||
import com.m2pool.system.api.model.OpenApiKeyInfo;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @Description 自定义请求头拦截器,将请求头数据封装到线程变量中方便后续获取使用
|
||||
* 同时获取当前用户有效时间并刷新有效期
|
||||
* @Date 2024/6/13 20:29
|
||||
* @Author dy
|
||||
*/
|
||||
public class OpenApiHeaderInterceptor implements AsyncHandlerInterceptor {
|
||||
|
||||
private static final Map<String, AtomicInteger> limitCounter = new ConcurrentHashMap<>();
|
||||
private static final int MAX_REQUESTS_SECOND = 5;
|
||||
private static final int MAX_REQUESTS_DAY = 5000;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
System.out.println("调用Oapi拦截器,路径:"+request.getRequestURI());
|
||||
System.out.println(IpUtils.getIpAddr(request));
|
||||
if(!(handler instanceof HandlerMethod)){
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//System.out.println("OpenApiHeaderInterceptor.preHandle 开始赋值");
|
||||
|
||||
OpenApiContextHolder.setApiUser(ServletUtils.getHeader(request, OpenApiKeyConstants.API_USER));
|
||||
OpenApiContextHolder.setApiIp(ServletUtils.getHeader(request,OpenApiKeyConstants.API_IP));
|
||||
OpenApiContextHolder.setApiKey(ServletUtils.getHeader(request,OpenApiKeyConstants.API_KEY));
|
||||
|
||||
String apiKey = OpenApiUtils.getApiKey();
|
||||
if(StringUtils.isNotEmpty(apiKey)){
|
||||
|
||||
OpenApiKeyInfo apiKeyInfo = ApiUtil.getApiKeyInfo(apiKey);
|
||||
if(StringUtils.isNotNull(apiKeyInfo)){
|
||||
|
||||
//验证当前用户有效时间
|
||||
ApiUtil.verifyApiKeyExpire(apiKeyInfo);
|
||||
OpenApiContextHolder.set(OpenApiKeyConstants.API_KEY_INFO,apiKeyInfo);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
|
||||
OpenApiContextHolder.remove();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.m2pool.common.security.service;
|
||||
|
||||
import com.m2pool.common.core.constant.CacheConstants;
|
||||
import com.m2pool.common.core.constant.OpenApiKeyConstants;
|
||||
import com.m2pool.common.core.utils.OpenApiJwtUtils;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.ip.IpUtils;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.OpenApiUtils;
|
||||
import com.m2pool.system.api.model.OpenApiKeyInfo;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @Description open api key 验证处理
|
||||
* @Date 2024/6/13 21:06
|
||||
* @Author dy
|
||||
*/
|
||||
@Component
|
||||
public class OpenApiService {
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/** 秒 */
|
||||
protected static final long MILLIS_SECOND = 1000;
|
||||
|
||||
/** 分 */
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
|
||||
|
||||
/** 过期时间/缓存有效期 设置为1年*/
|
||||
private final static long expireTime = CacheConstants.EXPIRATION * 2;
|
||||
|
||||
/** 权限缓存前缀 */
|
||||
private final static String ACCESS_API_KEY = CacheConstants.OPEN_API_KEY;
|
||||
|
||||
/** 缓存刷新时间 */
|
||||
private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*/
|
||||
public Map<String, Object> createApiKey(OpenApiKeyInfo openApiKey)
|
||||
{
|
||||
String apiKey = openApiKey.getKey();
|
||||
System.out.println("生成的apiKey:"+apiKey);
|
||||
String user = openApiKey.getUser();
|
||||
String apiIp = openApiKey.getApiIp();
|
||||
openApiKey.setKey(apiKey);
|
||||
openApiKey.setUser(user);
|
||||
if (StringUtils.isNotBlank(apiIp)){
|
||||
openApiKey.setApiIp(apiIp);
|
||||
}else {
|
||||
openApiKey.setApiIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
|
||||
}
|
||||
|
||||
refreshApiKey(openApiKey);
|
||||
|
||||
// Jwt存储信息
|
||||
Map<String, Object> claimsMap = new HashMap<String, Object>();
|
||||
claimsMap.put(OpenApiKeyConstants.API_KEY, apiKey);
|
||||
claimsMap.put(OpenApiKeyConstants.API_USER, user);
|
||||
claimsMap.put(OpenApiKeyConstants.API_IP, openApiKey.getApiIp());
|
||||
|
||||
// 接口返回信息
|
||||
Map<String, Object> rspMap = new HashMap<String, Object>();
|
||||
rspMap.put("api_key", OpenApiJwtUtils.createApiKey(claimsMap));
|
||||
rspMap.put("uuid生成的api_key", apiKey);
|
||||
rspMap.put("expires_in", expireTime);
|
||||
rspMap.put("user", user);
|
||||
return rspMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取apiKey信息
|
||||
*
|
||||
* @return apiKey信息
|
||||
*/
|
||||
public OpenApiKeyInfo getOpenApiKeyInfo()
|
||||
{
|
||||
return getOpenApiKeyInfo(ServletUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取apiKey信息
|
||||
*
|
||||
* @return apiKey信息
|
||||
*/
|
||||
public OpenApiKeyInfo getOpenApiKeyInfo(HttpServletRequest request)
|
||||
{
|
||||
// 获取请求携带的令牌
|
||||
String apiKey = OpenApiUtils.getApiKey(request);
|
||||
return getOpenApiKeyInfo(apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public OpenApiKeyInfo getOpenApiKeyInfo(String apiKey)
|
||||
{
|
||||
OpenApiKeyInfo openApiKey = null;
|
||||
try
|
||||
{
|
||||
if (StringUtils.isNotEmpty(apiKey))
|
||||
{
|
||||
String key = OpenApiJwtUtils.getApiKey(apiKey);
|
||||
openApiKey = redisService.getCacheObject(getAPIRedisKey(key));
|
||||
return openApiKey;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.out.println("oapiService.getOpenApiKeyInfo 异常");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return openApiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户api_key信息
|
||||
*/
|
||||
public void setOpenApiKeyInfo(OpenApiKeyInfo openApiKey)
|
||||
{
|
||||
if (StringUtils.isNotNull(openApiKey) && StringUtils.isNotEmpty(openApiKey.getKey()))
|
||||
{
|
||||
refreshApiKey(openApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除api_key缓存信息
|
||||
*/
|
||||
public void delOpenApiKey(String apiKey)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(apiKey))
|
||||
{
|
||||
String key = OpenApiJwtUtils.getApiKey(apiKey);
|
||||
//System.out.println("用户登出,从redis删除key:"+api_key:...);
|
||||
redisService.deleteObject(getAPIRedisKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌有效期,相差不足120分钟,自动刷新缓存
|
||||
*
|
||||
* @param openApiKey
|
||||
*/
|
||||
public void verifyApiKey(OpenApiKeyInfo openApiKey)
|
||||
{
|
||||
long expireTime = openApiKey.getExpireTime();
|
||||
long currentTime = System.currentTimeMillis();
|
||||
//可以考虑直接刷新缓存 而不是小于才刷新缓存
|
||||
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
|
||||
{
|
||||
refreshApiKey(openApiKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新api-key有效期
|
||||
*
|
||||
* @param openApiKey api-key请求信息
|
||||
*/
|
||||
public void refreshApiKey(OpenApiKeyInfo openApiKey)
|
||||
{
|
||||
openApiKey.setLoginTime(System.currentTimeMillis());
|
||||
openApiKey.setExpireTime(openApiKey.getLoginTime() + expireTime * MILLIS_MINUTE);
|
||||
// 根据uuid将api-key缓存
|
||||
String apiKey = getAPIRedisKey(openApiKey.getKey());
|
||||
redisService.setCacheObject(apiKey, openApiKey, expireTime, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private String getAPIRedisKey(String token)
|
||||
{
|
||||
return ACCESS_API_KEY + token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.m2pool.common.security.service;
|
||||
|
||||
import com.m2pool.common.core.constant.CacheConstants;
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.utils.JwtUtils;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.utils.ip.IpUtils;
|
||||
import com.m2pool.common.core.utils.uuid.IdUtils;
|
||||
import com.m2pool.common.redis.service.RedisService;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @Description token验证处理
|
||||
* @Date 2024/6/13 21:06
|
||||
* @Author dy
|
||||
*/
|
||||
@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);
|
||||
rspMap.put("userName", loginUser.getUsername());
|
||||
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);
|
||||
//System.out.println("用户登出,从redis删除key:"+userkey);
|
||||
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,86 @@
|
||||
package com.m2pool.common.security.utils;
|
||||
|
||||
|
||||
import com.m2pool.common.core.constant.OpenApiKeyConstants;
|
||||
import com.m2pool.common.core.constant.TokenConstants;
|
||||
import com.m2pool.common.core.context.OpenApiContextHolder;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.system.api.model.OpenApiKeyInfo;
|
||||
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @Description 权限获取工具类
|
||||
* @Date 2024/6/11 16:58
|
||||
* @Author dy
|
||||
*/
|
||||
public class OpenApiUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取api用户
|
||||
*/
|
||||
public static String getApiUser()
|
||||
{
|
||||
return OpenApiContextHolder.getApiUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取api绑定的ip
|
||||
*/
|
||||
public static String getApiIp()
|
||||
{
|
||||
return OpenApiContextHolder.getApiIp();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取api key
|
||||
*/
|
||||
public static String getKey()
|
||||
{
|
||||
return OpenApiContextHolder.getApiKey();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取apiKey对应的apiKey详细信息
|
||||
*/
|
||||
public static OpenApiKeyInfo getOpenApiKeyInfo()
|
||||
{
|
||||
OpenApiKeyInfo info = OpenApiContextHolder.get(OpenApiKeyConstants.API_KEY_INFO, OpenApiKeyInfo.class);
|
||||
return info;
|
||||
}
|
||||
/**
|
||||
* 获取请求token
|
||||
*/
|
||||
public static String getApiKey()
|
||||
{
|
||||
return getApiKey(ServletUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据request获取请求api-key
|
||||
*/
|
||||
public static String getApiKey(HttpServletRequest request)
|
||||
{
|
||||
// 从header获取token标识
|
||||
String token = request.getHeader(TokenConstants.API_KEY);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param user 用户邮箱
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isAdmin(String user)
|
||||
{
|
||||
return StringUtils.isNotBlank(user) && "".equals(user);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.m2pool.common.security.utils;
|
||||
|
||||
import com.m2pool.common.core.constant.SecurityConstants;
|
||||
import com.m2pool.common.core.constant.TokenConstants;
|
||||
import com.m2pool.common.core.context.SecurityContextHolder;
|
||||
import com.m2pool.common.core.utils.ServletUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.system.api.model.LoginUser;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @Description 权限获取工具类
|
||||
* @Date 2024/6/11 16:58
|
||||
* @Author dy
|
||||
*/
|
||||
public class SecurityUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户ID
|
||||
*/
|
||||
public static Long getUserId()
|
||||
{
|
||||
return SecurityContextHolder.getUserId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户名称
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成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,7 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.m2pool.common.security.config.WebMvcConfig,\
|
||||
com.m2pool.common.security.service.TokenService,\
|
||||
com.m2pool.common.security.service.OpenApiService,\
|
||||
com.m2pool.common.security.aspect.PreAuthAspect,\
|
||||
com.m2pool.common.security.aspect.InnerAuthAspect,\
|
||||
com.m2pool.common.security.handler.GlobalExceptionHandler
|
||||
Reference in New Issue
Block a user