springcloud整合gateway+sa-token做登录验证(干货)
一、什么是sa-token?
sa-token 是一个轻量级 java 权限认证框架,主要解决:登录认证、权限认证、单点登录、oauth2.0、分布式session会话、微服务网关鉴权 等一系列权限相关问题,在这里我就不详细介绍了,想要了解的同学可以去查看官方文档 https://sa-token.cc/doc.html
二、关于gateway
gateway网关是我们服务的守门神,是统一入口处
核心功能特性:
1.请求路由:通过特定的规则将请求转发到对应的微服务
2.权限控制:进入不同微服务之前都要进行一个验证
3.限流:当请求流量过高时就要限流
三、具体步骤
项目结构
1、配置网关服务
在gateway模块引入依赖
<?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>data_factory_cloud</artifactid>
<groupid>com.wjk</groupid>
<version>1.0-snapshot</version>
</parent>
<modelversion>4.0.0</modelversion>
<artifactid>data_factory_cloud_gateway</artifactid>
<dependencies>
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-gateway</artifactid>
</dependency>
<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>com.alibaba.cloud</groupid>
<artifactid>spring-cloud-starter-alibaba-nacos-discovery</artifactid>
</dependency>
<!-- httpclient依赖,缺少此依赖api网关转发请求时可能发生503错误 -->
<dependency>
<groupid>org.apache.httpcomponents</groupid>
<artifactid>httpclient</artifactid>
<version>4.5.13</version>
</dependency>
<!--redis坐标 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>
<dependency>
<groupid>org.apache.commons</groupid>
<artifactid>commons-pool2</artifactid>
<version>2.9.0</version> <!-- 请使用最新的版本号 -->
</dependency>
</dependencies>
</project>
在application.yml文件里面进行配置
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: source_database
uri: lb://source_database #负载均衡
predicates: #路由断言,判断请求是否符合规则
- path=/sourcedatabase/** #路径断言,判断路径是否是以/user开头,如果是符合
# filters: #过滤器
# - addrequestheader=truth,itcast is freaking aowsome!
- id: code
uri: lb://code
predicates:
- path=/code/**
- id: user
uri: lb://user
predicates:
- path=/user/**
- id: data_asset
uri: lb://data_asset
predicates:
- path=/dataasset/**
- id: data_standard
uri: lb://data_standard
predicates:
- path=/order/**
- id: sourceapi
uri: lb://data_standard
predicates:
- path=/sourceapi/**
redis:
host: 192.168.246.133
port: 6379
timeout: 10s
# password: 123456
# database: 0
lettuce:
pool:
max-active: 10
max-wait: -1
max-idle: 16
min-idle: 8
# sa-token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: sa-token-authorization
# token有效期,单位s 默认30天, -1代表永不过期
timeout: 2592000
# token风格
token-style: random-32
# 是否尝试从 header 里读取 token
is-read-head: true
# 是否开启自动续签
auto-renew: true
# 临时有效期,单位s,例如将其配置为 1800 (30分钟),代表用户如果30分钟无操作,则此token会立即过期
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时同端互斥)
is-concurrent: true
# 配置 sa-token 单独使用的 redis 连接
alone-redis:
# redis数据库索引(默认为0)
database: 0
# redis服务器地址
host: 192.168.246.133
# redis服务器连接端口
port: 6379
# redis服务器连接密码(默认为空)
# password:
# 连接超时时间
timeout: 10s
新建类satokenconfigure,实现网关拦截
package com.wjk.config;
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;
/**
* @author yangboss
* @title: satokenconfigure
* @projectname meta
* @description: todo
* @date 2022/8/18 10:12
*/
@configuration
public class satokenconfigure {
// 注册 sa-token全局过滤器
@bean
public sareactorfilter getsareactorfilter() {
return new sareactorfilter()
// 拦截地址
.addinclude("/**")
// 开放地址
.addexclude("/user/login")
.addexclude("/user/sendemail")
.addexclude("/user/regist")
// 鉴权方法:每次访问进入
.setauth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/dologin 用于开放登录
sarouter.match("/**", "/user/login", r -> stputil.checklogin());
sarouter.match("/**", "/user/regist", r -> stputil.checklogin());
sarouter.match("/**", "/user/sendemail", r -> stputil.checklogin());
// // 角色认证 -- 拦截以 admin 开头的路由,必须具备 admin 角色或者 super-admin 角色才可以通过认证
// sarouter.match("/admin/**", r -> stputil.checkroleor("admin", "super-admin"));
// // 权限认证 -- 不同模块, 校验不同权限
// sarouter.match("/meta-system/**", r -> stputil.checkpermission("system-no"));
// sarouter.match("/admin/**", r -> stputil.checkpermission("admin"));
// 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");
// return new resultjsonutil().fail(e.getmessage());
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();
});
}
}
2、配置用户服务
引入依赖
<?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>data_factory_cloud</artifactid>
<groupid>com.wjk</groupid>
<version>1.0-snapshot</version>
</parent>
<modelversion>4.0.0</modelversion>
<artifactid>data_factory_cloud_user</artifactid>
<dependencies>
<dependency>
<groupid>com.wjk</groupid>
<artifactid>data_factory_cloud_common</artifactid>
<version>1.0-snapshot</version>
</dependency>
<!-- sa-token权限认证框架 -->
<dependency>
<groupid>cn.dev33</groupid>
<artifactid>sa-token-spring-boot-starter</artifactid>
<version>1.37.0</version>
</dependency>
<!-- sa-token 整合 redis (使用 jackson 序列化方式) -->
<dependency>
<groupid>cn.dev33</groupid>
<artifactid>sa-token-redis-jackson</artifactid>
<version>1.37.0</version>
</dependency>
<!-- spring boot starter mail -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-mail</artifactid>
</dependency>
</dependencies>
</project>
在yml文件里面配置
# sa-token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: sa-token-authorization
# token有效期,单位s 默认30天, -1代表永不过期
timeout: 3600
# token风格
token-style: random-32
# 是否尝试从 header 里读取 token
is-read-head: true
# 是否开启自动续签
auto-renew: true
# 临时有效期,单位s,例如将其配置为 1800 (30分钟),代表用户如果30分钟无操作,则此token会立即过期
activity-timeout: 1800
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时同端互斥)
is-concurrent: true
# 配置 sa-token 单独使用的 redis 连接
alone-redis:
# redis数据库索引(默认为0)
database: 0
# redis服务器地址
host: 192.168.246.133
# redis服务器连接端口
port: 6379
# redis服务器连接密码(默认为空)
# password:
# 连接超时时间
timeout: 30s
编写登录接口
userservice
package com.wjk.service;
import cn.dev33.satoken.util.saresult;
import com.baomidou.mybatisplus.extension.service.iservice;
import com.wjk.entity.user;
import com.wjk.result.r;
import com.wjk.vo.uservo;
public interface userservice extends iservice<user> {
//发送验证码邮件给指定邮箱
r sendmimemail(string email);
//随机生成6位数字验证码
string randomcode();
//用户注册,验证验证码并保存用户信息
r registered(uservo uservo);
//登录
saresult login(string email, string password) throws exception;
//登出
saresult logout();
}
userserviceimpl
@override
public saresult login(string email, string password) throws exception {
//根据用户名从数据库中查询
user user = this.getone(wrappers.lambdaquery(user.class).eq(user::getemail,email));
system.out.println(user);
if(user == null){
return saresult.error("用户名不存在");
}
//对登录密码进行加密
boolean result = md5util.passwordverify(password, user.getpassword(), md5util.md5key);
if(result){
//根据用户id登录,第1步,先登录上
stputil.login(user.getid());
// 第2步,获取 token 相关参数
satokeninfo tokeninfo = stputil.gettokeninfo();
// 第3步,返回给前端
return saresult.data(tokeninfo);
}
return saresult.error("密码错误");
}
user
package com.wjk.entity;
import com.baomidou.mybatisplus.annotation.idtype;
import com.baomidou.mybatisplus.annotation.tablefield;
import com.baomidou.mybatisplus.annotation.tableid;
import com.baomidou.mybatisplus.annotation.tablename;
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;
import java.util.date;
@tablename("users")
@data
@noargsconstructor
@allargsconstructor
public class user {
@tableid(value = "id",type = idtype.auto)
private integer id;
@tablefield("username")
private string username;
@tablefield("password")
private string password;
@tablefield("email")
private string email;
@tablefield("created_at")
private date createdat;
}
uservo
package com.wjk.vo;
import lombok.data;
@data
public class uservo {
private string username;
private string password;
private string email;
//验证码
private string code;
}
uservotouser
package com.wjk.vo;
import com.wjk.entity.user;
public class uservotouser {
/**
* 将表单中的对象转化为数据库中存储的用户对象,剔除表单中的验证码字段。
*
* @param uservo 表单中的对象
* @return 数据库中存储的用户对象
*/
public static user touser(uservo uservo) {
// 创建一个数据库中存储的对象
user user = new user();
// 赋值
user.setusername(uservo.getusername());
user.setpassword(uservo.getpassword());
user.setemail(uservo.getemail());
// 返回转化后的对象
return user;
}
}
md5加密
package com.wjk.utils;
import org.apache.commons.codec.digest.digestutils;
public class md5util {
// 实现一个md5加解密
public final static string md5key = "lalalalall";
/**
*
* @param strpwd 明文密码
* @param
* @return 密文
* @throws exception
*/
//用于注册时对密码进行加密
public static string md5(string strpwd,string key) throws exception{
// 获取加密后的字符串
string encodestr = digestutils.md5hex(strpwd + key);//调用加密的算法
return encodestr;
}
/**
* 用户登录,密码验证
* @param pwdstr 明文字符串
* @param oldpwd 密文字符串
* @return
*/
public static boolean passwordverify(string pwdstr,string oldpwd,string key) throws exception {
//在该方法中,不需要在外面做密码加密,登录时获取到当前用户输入的密码,在方法里进行加密
string md5pwd= md5(pwdstr,key);
if (md5pwd.equalsignorecase(oldpwd)){
return true;
}
return false;
}
}
controller
package com.wjk.controller;
import cn.dev33.satoken.util.saresult;
import com.wjk.service.userservice;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;
import javax.annotation.resource;
@restcontroller
@requestmapping("/user")
public class logincontroller {
@resource
private userservice userservice;
@getmapping("/login")
public saresult login(string email, string password) throws exception {
return userservice.login(email,password);
}
@getmapping("/logout")
public saresult logout(){
return userservice.logout();
}
}
测试
发送验证码
根据验证码进行注册
登录
我这里的端口号10010对应的是gateway模块里的yml文件里面配置的端口号,这就是统一入口处,由它进行请求转发,肯定不止向登录模块请求转发,还会向别的端口进行,那么配置也跟上面一样,在gateway里面的yml文件里面要配置
gateway:
routes:
- id: source_database
uri: lb://source_database
predicates: #路由断言,判断请求是否符合规则
- path=/sourcedatabase/** #路径断言,判断路径是否是以/user开头,如果是符合
# filters: #过滤器
# - addrequestheader=truth,itcast is freaking aowsome!
- id: code
uri: lb://code
predicates:
- path=/code/**
- id: user
uri: lb://user
predicates:
- path=/user/**
- id: data_asset
uri: lb://data_asset
predicates:
- path=/dataasset/**
- id: data_standard
uri: lb://data_standard
predicates:
- path=/order/**
- id: sourceapi
uri: lb://data_standard
predicates:
- path=/sourceapi/**
该项目具体代码在gitee
https://gitee.com/wjk0321/data_factory_cloud
uri: lb://code
predicates:
- path=/code/**
- id: user
uri: lb://user
predicates:
- path=/user/**
- id: data_asset
uri: lb://data_asset
predicates:
- path=/dataasset/**
- id: data_standard
uri: lb://data_standard
predicates:
- path=/order/**
- id: sourceapi
uri: lb://data_standard
predicates:
- path=/sourceapi/**
该项目具体代码在gitee
https://gitee.com/wjk0321/data_factory_cloud
发表评论