一:感谢大佬
本博客内容 参考了satoken官网实现,satoken官网地址:
https://sa-token.cc/doc.html#/micro/gateway-auth
二:项目层级介绍
- jinyi-gateway 网关服务
- jinyi-user-service 用户服务
2.1 jinyi-user-api
2.2 jinyi-user-client
2.3 jinyi-user-provider - jinyi-common 通用服务,定义了一些统一返回类,全局常量(R等)
项目层级关系截图:
三:项目具体介绍
3.1jinyi-gateway 网关服务
3.1.1 pom.xml
parent标签里只是指明了springboot springcloud springcloud-alibaba等版本信息和通用的像lombok等通用的类。
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>jinyi-up-project</artifactId>
<groupId>com.jinyi.up</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jinyi-gateway</artifactId>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<alibaba.nacos.version>2.1.0</alibaba.nacos.version>
<jinyi-up.version>1.0.0</jinyi-up.version>
<sa-token.version>1.34.0</sa-token.version>
</properties>
<dependencies>
<!--Spring Cloud & Alibaba-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<exclusions>
<!--Sa-Token 权限认证(Reactor响应式)-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--通用基础common-->
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-common-base</artifactId>
<version>${jinyi-up.version}</version>
</dependency>
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-client</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
核心maven依赖
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.34.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
config包中类:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
@RefreshScope
public class ExecutorConfig {
@Value("${executor.corePoolSize:4}")
private Integer corePoolSize;
@Value("${executor.queueCapacity:1000}")
private Integer queueCapacity;
@Value("${executor.maxPoolSize:6}")
private Integer maxPoolSize;
@Value("${executor.keepAliveSeconds:30}")
private Integer keepAliveSeconds;
/**
* 线程池参数定义
* @return
*/
@Bean
public ThreadPoolTaskExecutor AsyncThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("async-task-executor-");
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setMaxPoolSize(maxPoolSize);
//线程大于coreSize,多余线程数超过30s则销毁
executor.setKeepAliveSeconds(keepAliveSeconds);
// 设置拒绝策略,调用当前线程执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import java.util.stream.Collectors;
/**
* gateway是基于WebFlux的一个响应式组件,HttpMessageConverters不会像Spring Mvc一样自动注入,需要我们手动配置
*
*/
@Configuration
public class HttpMessageConvertersConfigure {
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
}
其中最核心的SaTokenConfigures类
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.server.ServerWebExchange;
@Configuration
public class SaTokenConfigures {
//需要放行的接口[白名单]
private static final String[] PERMITURL = new String[]{
"/user-api/jinyiUser/accountLogin"
};
// 注册 Sa-Token全局过滤器
@Bean
public SaReactorFilter getSaServletFilter() {
return new SaReactorFilter()
// 拦截地址
.addInclude("/**")
// 开放地址【白名单】
// .addExclude(PERMITURL)
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
// SaRouter.match("/**", "/account-service/account/userLogin", r -> StpUtil.checkLogin());
SaRouter.match("/**")
.notMatch("/user-api/jinyiUser/accountLogin",
"/**/swagger-resources/**", "/**/webjars/**",
"/**/swagger-ui.html/**",
"/**/doc.html/**", "/**/error",
"/**/favicon.ico").check(r -> StpUtil.checkLogin());
// 权限认证 -- 不同模块, 校验不同权限
SaRouter.match("/jinyiUser/**", r -> StpUtil.checkRole("admin"));
// SaRouter.match("/user-api/jinyiUser/**", r -> StpUtil.checkRole("admin"));
// SaRouter.match("/jinyiUser/**", r -> StpUtil.checkPermission("system"));
// SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
// SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
// ...
})
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> {
// 设置错误返回格式为JSON
ServerWebExchange exchange = SaReactorSyncHolder.getContext();
exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
//登录或者权限相关的resp json 都是在下面的代码里封装返回的
return SaResult.error(e.getMessage());
})
.setBeforeAuth(obj -> {
// ---------- 设置跨域响应头 ----------
SaHolder.getResponse()
// 允许指定域访问跨域资源
.setHeader("Access-Control-Allow-Origin", "*")
// 允许所有请求方式
.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
// 有效时间
.setHeader("Access-Control-Max-Age", "3600")
// 允许的header参数
.setHeader("Access-Control-Allow-Headers", "*");
// 如果是预检请求,则立即返回到前端
SaRouter.match(SaHttpMethod.OPTIONS).free(r -> System.out.println("--------OPTIONS预检请求,不做处理")).back();
});
}
@Bean
@Primary
public SaTokenConfig getSaTokenConfigPrimary() {
SaTokenConfig config = new SaTokenConfig();
config.setTokenName("token"); // token名称 (同时也是cookie名称)
config.setTimeout(30 * 24 * 60 * 60); // token有效期,单位s 默认30天
config.setActivityTimeout(-1); // token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
config.setIsConcurrent(true); // 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
config.setIsShare(true); // 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
config.setTokenStyle("uuid"); // token风格
config.setIsLog(false); // 是否输出操作日志
return config;
}
}
exception包GlobalException类
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.jinyi.up.common.base.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler
public R handlerException(Exception e) {
e.printStackTrace();
// 不同异常返回不同状态码
R r = null;
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
log.error("登录异常:{}",e.getMessage());
R.failed(ee.getMessage());
}
else if(e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
R.failed("无此角色:" + ee.getRole());
}
else if(e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
R.failed("无此权限:" + ee.getMessage());
}
// else if(e instanceof DisableLoginException) { // 如果是被封禁异常
// R.failed("账号被封禁:" + e.getDisableTime() + "秒后解封");
// }
else { // 普通异常, 输出:500 + 异常信息
R.failed();
}
// 返回给前端
return r;
}
}
service包中
import cn.dev33.satoken.stp.StpInterface;
import com.google.common.collect.Lists;
import com.jinyi.up.common.base.result.R;
import com.jinyi.up.feignclient.UserProvideFeignClient;
import com.jinyi.up.user.vo.JinyiRoleVO;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
public class StpInterfaceImpl implements StpInterface {
@Resource
private UserProvideFeignClient userFeignClient;
@Resource
@Qualifier(value = "AsyncThreadPoolTaskExecutor")
ThreadPoolTaskExecutor executor;
/**
* 返回指定账号id所拥有的权限码集合
*
* @param loginId 账号id
* @param loginType 账号类型
* @return 该账号id具有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 返回此 loginId 拥有的权限列表,没有设置权限表。此处为伪代码
List<String> strs = new ArrayList<>();
strs.add("system");
return strs;
}
/**
* 返回指定账号id所拥有的角色标识集合
*
* @param loginId 账号id
* @param loginType 账号类型
* @return 该账号id具有的角色标识集合
*/
@SneakyThrows
@Override
public List<String> getRoleList(Object loginId, String loginType) {
List<String> result = Lists.newArrayList();
CompletableFuture<R> AsyncResp = CompletableFuture.supplyAsync(() -> {
return userFeignClient.getUserRoleByUserId((Long) loginId);
}, executor);
R<List<JinyiRoleVO>> resp = AsyncResp.get();
if (R.isSuccess(resp) && !CollectionUtils.isEmpty(resp.getData())) {
result = resp.getData().stream().map(JinyiRoleVO::getCode).collect(Collectors.toList());
}
return result;
}
}
gateway 项目启动类
import com.jinyi.up.feignclient.UserProvideFeignClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
//@FeignClinet 和 @EnableFeignClients 不是同一个包。指定basePackages包扫描路径,指定basePackageClasses扫描类
@EnableFeignClients(basePackageClasses = {UserProvideFeignClient.class})
public class JinyiGateWayApplication {
public static void main(String[] args) {
SpringApplication.run(JinyiGateWayApplication.class,args);
}
}
yml配置文件bootstrap.yml 和 application-dev.yml
bootstrap.yml 如下
server:
port: 8070
spring:
application:
name: jinyi-gateway
main:
allow-bean-definition-overriding: true
profiles:
active: dev
cloud:
nacos:
# 注册中心
discovery:
server-addr: 127.0.0.1:8848
# 配置中心
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: GATEWAY_GROUP # 配置中心 配置中心是空的
# shared-configs[0]:
# data-id: jinyi-common.yaml
# group: COMMON_GROUP
# refresh: true
#gateway的配置
gateway:
# discovery:
# locator:
# enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
# lower-case-service-id: true
#路由规则
routes:
- id: jinyi-user-provider # 路由的id,没有规定规则但要求唯一,建议配合服务名
uri: lb://jinyi-user-provider #uri: lb://路由项目的spring.application.name
#断言 用于路由规则的匹配 路径相匹配的进行路由
predicates:
- Path=/user-api/** #用户路由
filters:
- StripPrefix=1 #去掉路由url中的前缀 /jinyi-user
# 关键在下面一句,值为true则开启认证,false则不开启
# 这种配置方式和spring cloud gateway内置的GatewayFilterFactory一致
# - Authorize=false #自定义的过滤工厂
logging:
level:
spring.: DEBUG
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: token
# token有效期,单位秒,默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期),单位秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# 是否从cookie中读取token
is-read-cookie: false
# 是否从head中读取token
is-read-header: true
# token前缀
# token-prefix: Bearer
application-dev.yml如下
spring:
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 3000ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
3.2jinyi-user-service服务
3.2.1 jinyi-user-api项目
pom.xml文件
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jinyi-user-service</artifactId>
<groupId>com.jinyi.up</groupId>
<version>1.0.0</version>
</parent>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>jinyi-user-api</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
dto 和 vo目录下的类分别是
import io.swagger.annotations.ApiModel;
import lombok.Data;
@Data
@ApiModel(value = "用户账号登录")
public class UserAccountLoginDTO {
private String account;
private String password;
}
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("用户角色VO")
public class JinyiRoleVO {
@ApiModelProperty("主键id")
private Long id;
@ApiModelProperty("角色名称")
private String name;
@ApiModelProperty("角色编码")
private String code;
@ApiModelProperty("角色状态:1-正常;0-停用")
private Boolean status;
}
jinyi-user-client项目:
pom.xml
<?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>jinyi-user-service</artifactId>
<groupId>com.jinyi.up</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jinyi-user-client</artifactId>
<name>jinyi-user-client</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<jinyi-up.version>1.0.0</jinyi-up.version>
</properties>
<dependencies>
<!--引入base common-->
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-common-base</artifactId>
<version>${jinyi-up.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<!-- <optional>true</optional>-->
</dependency>
<!-- openfeign依赖 1. http客户端选择okhttp 2. loadbalancer替换ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
核心feign类
import com.jinyi.up.common.base.result.R;
import com.jinyi.up.feignclient.fallback.UserProvideFeignFallback;
import com.jinyi.up.user.dto.UserAccountLoginDTO;
import com.jinyi.up.user.vo.JinyiRoleVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* @FeignClient注解 value 指定的要代理的服务的spring:application:name配置信息
* path 值的被代理的服务的统一路径
*/
@FeignClient(value = "jinyi-user-provider", path = "/jinyiUser",
fallback = UserProvideFeignFallback.class) //实现UserProvideFeign服务降级
// fallbackFactory = UserProvideFeignFallbackFactory.class)
public interface UserProvideFeignClient {
@PostMapping("/accountLogin")
R phoneLogin(@RequestBody UserAccountLoginDTO userPhoneLoginDTO);
@GetMapping("/getUserRoleByUserId/{userId}")
R<List<JinyiRoleVO>> getUserRoleByUserId(@PathVariable("userId") Long userId);
@PostMapping("/userLogout")
R userLogout();
}
jinyi-user-provider项目
pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jinyi-user-service</artifactId>
<groupId>com.jinyi.up</groupId>
<version>1.0.0</version>
</parent>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-provider</artifactId>
<version>1.0.0</version>
<name>jinyi-user-provider</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<jinyi-up.version>1.0.0</jinyi-up.version>
<sa-token.version>1.34.0</sa-token.version>
</properties>
<dependencies>
<!-- 配置读取 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- Spring Cloud & Alibaba -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--common基础依赖-->
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-common-redis</artifactId>
<version>${jinyi-up.version}</version>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-common-mybatis-plus</artifactId>
<version>${jinyi-up.version}</version>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-common-web</artifactId>
<version>${jinyi-up.version}</version>
</dependency>
<!-- sa-token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
</dependency>
<dependency>
<groupId>com.jinyi.up</groupId>
<artifactId>jinyi-user-api</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
启动类
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(value = "com.jinyi.up.user.mapper")
public class JinyiUserProviderApplication {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(JinyiUserProviderApplication.class, args);
Environment env = applicationContext.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
// String activePrn ofile = SpringContext.getActiveProfile();
log.info("\n----------------------------------------------------------\n\t" +
"Application is running! Access URLs:\n\t" +
"Doc: \t\thttp://" + ip + ":" + port + "/doc.html\n\t" +
"Swagger: \thttp://" + ip + ":" + port + "/swagger-ui/\n\t" +
"----------------------------------------------------------");
}
}
yml配置文件bootstrap.yml 和 application-dev.yml
bootstrap.yml 如下
server:
port: 8060
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
application:
name: jinyi-user-provider
main:
allow-bean-definition-overriding: true
profiles:
active: dev
cloud:
nacos:
# 注册中心
discovery:
server-addr: 1127.0.0.1:8848
# 配置中心
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: USER_GROUP
# shared-configs[0]:
# data-id: jinyi-common.yaml
# group: COMMON_GROUP
# refresh: true
logging:
level:
spring.: DEBUG
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: token
# token有效期,单位秒,默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期),单位秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# 是否从cookie中读取token
is-read-cookie: false
# 是否从head中读取token
is-read-header: true
# token前缀
# token-prefix: Bearer
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
datasource:
url: "jdbc:mysql://127.0.0.1:3306/jinyi_user_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true"
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# driver-class-name: com.mysql.jdbc.Driver
hikari:
minimum-idle: 16
maximum-pool-size: 40
connection-timeout: 3000
idle-timeout: 50000
validation-timeout: 60000
max-lifetime: 86370
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: 3000ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
核心登录方法:文章来源:https://www.toymoban.com/news/detail-419176.html
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.jinyi.up.common.base.result.R;
import com.jinyi.up.user.dto.UserAccountLoginDTO;
import com.jinyi.up.user.entity.JinyiRole;
import com.jinyi.up.user.entity.JinyiUser;
import com.jinyi.up.user.entity.JinyiUserRole;
import com.jinyi.up.user.service.IJinyiRoleService;
import com.jinyi.up.user.service.IJinyiUserRoleService;
import com.jinyi.up.user.service.IJinyiUserService;
import com.jinyi.up.user.vo.JinyiRoleVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Api(tags = "用户相关接口")
@RestController
@RequestMapping("/jinyiUser")
public class JinyiUserController {
@Resource //用户服务
private IJinyiUserService userService;
@Resource //用户角色关联表服务
private IJinyiUserRoleService userRoleService;
@Resource //用户角色服务
private IJinyiRoleService roleService;
@ApiOperation(value = "用户账号登录", httpMethod = "POST")
@PostMapping("/accountLogin")
public R accountLogin(@RequestBody UserAccountLoginDTO userPhoneLoginDTO) {
String account = userPhoneLoginDTO.getAccount();
String password = userPhoneLoginDTO.getPassword();
JinyiUser jinyiUser = userService.accountLogin(account, password);
if (null != jinyiUser) {
//密码校验通过后,直接调用登录
StpUtil.login(jinyiUser.getId(), "PC");
//登录成功后,返回相关的token信息
return R.ok(StpUtil.getTokenInfo());
}
return R.failed("账号密码错误");
}
@ApiOperation(value = "查询用户对应的角色", httpMethod = "GET")
@GetMapping("/getUserRoleByUserId/{userId}")
public R<List<JinyiRoleVO>> getUserRoleByUserId(@PathVariable("userId") Long userId) {
List<JinyiUserRole> userRoles = userRoleService.getByUserId(userId);
if (CollectionUtils.isEmpty(userRoles)) {
return R.ok();
}
Set<Integer> roleIds = userRoles.stream().map(JinyiUserRole::getRoleId).collect(Collectors.toSet());
List<JinyiRole> roles = roleService.getByRoleIds(roleIds);
if (CollectionUtils.isEmpty(roles)) {
return R.ok();
}
List<JinyiRoleVO> jinyiRoleVOS = BeanUtil.copyToList(roles, JinyiRoleVO.class);
return R.ok(jinyiRoleVOS);
}
@ApiOperation(value = "退出登录", httpMethod = "POST")
@PostMapping("/userLogout")
public R userLogout() {
// 当前会话注销登录
StpUtil.logout();
return R.ok();
}
}
最后测试
1.通过网关转发的登录
http://localhost:8070/user-api/jinyiUser/accountLogin
2.网关转发的其他需要登录带token的访问
文章来源地址https://www.toymoban.com/news/detail-419176.html
到了这里,关于satoken+ gateway网关统一鉴权 初版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!