java 单机接口限流处理方案
272
2022-11-05
Spring Cloud Gateway 使用JWT工具类做用户登录校验功能
1. JWT测试
/**
* @Auther: csp1999
* @Date: 2021/01/24/19:29
* @Description: JWT测试
*/
public class JwtTest {
/**
* 创建Jwt令牌:
*
* JWT = 头部Header + 载荷playload + 签名signature
*/
@Test
public void testCreateJwt() {
// 构建jwt令牌
// 1.头部Header: 描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等
JwtBuilder builder = Jwts.builder()
.setId("8989") // 设置令牌唯一编号
.setIssuer("csp1999") // 设置令牌颁发者
.setSubject("JWT加密测试") // 设置令牌主题 可以是jsON数据
.setIssuedAt(new Date()) // 设置令牌签发日期
.setExpiration(nehttp://w Date(System.currentTimeMillis() + 1000 * 60 * 3));// 设置令牌过期时间 3分钟
// 2.自定义载荷playload: 存放有效信息的地方
Map
userInfo.put("username","csp");
userInfo.put("password","123456");
userInfo.put("school","河南科技大学");
userInfo.put("age","22");
// 将载荷添加到JWT令牌中
builder.addClaims(userInfo);
// 3.为令牌设置 签名signature
builder.signWith(SignatureAlgorithm.HS256, "haust");// 设置令牌的签名 使用HS256算法,并设置SecretKey密钥(字符串)
// 构建 并返回一个字符串
String jwtStr = builder.compact();
System.out.println(jwtStr);
}
/**
* 解析Jwt令牌数据
*/
@Test
public void testParseJwt() {
// jwt字符串
String jwtStr = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4OTg5IiwiaXNzIjoiY3NwMTk5OSIsInN1YiI6IkpXVOWKoOWvhua1i-ivlSIsImlhdCI6MTYxMTQ4ODc1MSwiZXhwIjoxNjExNDg4OTMxLCJwYXNzd29yZCI6IjEyMzQ1NiIsInNjaG9vbCI6Iuays-WNl-enkeaKgOWkp-WtpiIsImFnZSI6IjIyIiwidXNlcm5hbWUiOiJjc3AifQ.uH28G9MSHfzaKBAOyr8AdksYLVvy8O5P8g7TORZIUFY";
// 解析jwt字符串
Claims claims = Jwts.parser().
setSigningKey("haust"). // 密钥(盐)
parseClaimsJws(jwtStr). // 要解析的令牌对象
getBody(); // 获取解析后的结果
// {jti=8989, iss=csp1999, sub=JWT加密测试, iat=1611488751, exp=1611488931, password=123456, school=河南科技大学, age=22, username=csp}
System.out.println(claims);
}
}
2. JWT工具类
/**
* @Auther: csp1999
* @Date: 2021/01/24/19:29
* @Description: JWT工具类
*/
public class JwtUtil {
// 有效期为
public static final Long JWT_TTL = 3600000L;// 60 * 60 * 1000 一个小时
// Jwt令牌信息
public static final String JWT_KEY = "itcast";
/**
* 生成令牌
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
// 指定算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 当前系统时间
long nowMillis = System.currentTimeMillis();
// 令牌签发时间
Date now = new Date(nowMillis);
// 如果令牌有效期为null,则默认设置有效期1小时
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
// 令牌过期时间设置
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
// 生成秘钥
SecretKey secretKey = generalKey();
// 封装Jwt令牌信息
JwtBuilder builder = Jwts.builder()
.setId(id) //唯一的ID
.setSubject(subject) // 主题 可以是JSON数据
.setIssuer("admin") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) // 签名算法以及密匙
.setExpiration(expDate); // 设置过期时间
return builder.compact();
}
/**
* 生成加密 secretKey
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析令牌数据
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
public static void main(String[] args) {
String jwt = JwtUtil.createJWT("weiyibiaoshi", "aaaaaa", null);
System.out.println(jwt);
try {
Claims claims = JwtUtil.parseJWT(jwt);
System.out.println(claims);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 用户登录校验
3.1 网关过滤器
/**
* @Auther: csp1999
* @Date: 2021/01/24/20:17
* @Description: 授权过滤器
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
// 令牌头名字
private static final String AUTHORIZE_TOKEN = "Authorization";
/**
* 全局过滤器
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono
// 获取Request、Response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 获取请求的URI
String path = request.getURI().getPath();
// 如果是登录、goods等开放的微服务[这里的goods部分开放],则直接放行,这里不做完整演示,完整演示需要设计一套权限系统
// 未登录下只放行登录和搜索
if (path.startsWith("/api/user/login") || path.startsWith("/api/brand/search/")) {
// 放行
Mono
return filter;
}
// 从头文件中获取的令牌信息
String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN);
// 如果为true:说明令牌在头文件中, false:令牌不在头文件中,将令牌封装入头文件,再传递给其他微服务
boolean hasToken = true;
// 如果头文件中没有令牌信息,则从请求参数中获取
if (StringUtils.isEmpty(token)) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
hasToken = false;
}
// 如果为空,则输出错误代码
if (StringUtils.isEmpty(token)) {
// 设置方法不允许被访问,405错误代码
response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
return response.setComplete();
}
// 如果不为空,则解析令牌数据
try {
Claims claims = JwtUtil.parseJWT(token);
} catch (Exception e) {
e.printStackTrace();
// 解析失败,响应401错误
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 放行之前,将令牌封装到头文件中(这一步是为了方便AUTH2校验令牌)
request.mutate().header(AUTHORIZE_TOKEN,token);
// 放行
return chain.filter(exchange);
}
/**
* 过滤器执行顺序
*
* @return
*/
@Override
public int getOrder() {
// 首位
return 0;
}
}
3.2 网关微服务application.yml
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" # 跨域处理 允许所有的域
allowedMethods: #支持的请求类型
- GET
- POST
- PUT
- DELETE
routes:
# 对接商品goods微服务路由相关配置
- id: changgou_goods_route
uri: lb://changgou-goods
predicates:
- Path=/api/brand/**,/api/category/**
filters:
- StripPrefix=1
- name: RequestRateLimiter # 请求数限流 名字不能随便写 ,使用默认的facatory
args:
# 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
key-resolver: "#{@ipKeyResolver}"
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶总容量
redis-rate-limiter.burstCapacity: 1
# 上面配置,表示1秒内,允许 1个请求通过,令牌桶的填充速率也是1秒钟添加1个令牌。
# 对接用户user微服务路由相关配置
- id: changgou_user_route
uri: lb://changgou-user
predicates:
- Path=/api/user/**,/api/address/**,/api/areas/**,/api/cities/**,/api/provinces/**
filters:
# user微服务真实请求中是没有/api的,所以这里StripPrefix=1
- StripPrefix=1
# 微服务名称
application:
name: changgou-gateway-web
# Redis配置
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 8.131.66.136
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: csp19990129
server:
port: 8001
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
management:
endpoint:
gateway:
enabled: true
web:
exposure:
include: true
3.3 网关微服务主启动类
/**
* @Auther: csp1999
* @Date: 2021/01/24/15:16
* @Description: 用户/前台微服务网关启动类
*/
@SpringBootApplication
@EnableEurekaClient
public class GatewayWebApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayWebApplication.class, args);
}
/**
* IP限流:由用户请求的IP创建创建用户唯一标识,进而根据IP进行限流操作
*
* @return
*/
@Bean(name = "ipKeyResolver")
public KeyResolver userKeyResolver() {
return new KeyResolver() {
@Override
public Mono
// 获取远程客户端IP
String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
System.out.println("hostName:" + hostName);
return Mono.just(hostName);
}
};
}
}
3.4 用户微服务编写登录代码
/**
* @Author: csp1999
* @Description: User 的Controller
* @Date 2021/1/14 0:18
*/
@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
/***
* 修改User数据
* @param user
* @param id
* @return
*/
@PutMapping(value = "/{id}")
public Result update(@RequestBody User user, @PathVariable String id) {
...
}
/***
* 新增User数据
* @param user
* @return
*/
@PostMapping
public Result add(@RequestBody User user) {
...
}
/***
* 根据ID查询User数据
* @param id
* @return
*/
@GetMapping("/{id}")
public Result
...
}
/***
* 查询User全部数据
* @return
*/
@GetMapping
public Result> findAll() {
...
}
/***
* 用户登录
* @param username
* @param password
* @param response
* @param request
* @return
*/
@RequestMapping("/login")
public Result
// 1.从数据库中查询用户名对应的用户的对象
User user = userService.findById(username);
if (user == null) {
// 2.判断用户是否为空 为空返回数据
return new Result
}
// 3.如果不为空 判断密码是否正确 若正确 则登录成功
if (BCrypt.checkpw(password, user.getPassword())) {
// 登录成功,讲用户信息存入map
Map
info.put("role", "USER");
info.put("success", "SUCCESS");
info.put("username", username);
// 3.1生成令牌
String jwt = JwtUtil.createJWT(UUID.randomUUID().toString(), JSON.toJSONString(info), null);
// 3.2设置jwt存入 cookie 中
Cookie cookie = new Cookie("Authorization", jwt);
response.addCookie(cookie);
// 3.3设置jwt存入头文件中
response.setHeader("Authorization", jwt);
return new Result
} else {
// 登录失败
return new Result
}
}
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~