今日总结
- java随笔录------网关认证,为什么使用Spring Security来获取用户身份,而不是用Threadlocal来直接获取呢
- AI随探录------
- 代码随想录------组合问题2(回溯去重)
目录
[为什么使用Spring Security来获取用户身份,而不是用Threadlocal来直接获取呢?](#为什么使用Spring Security来获取用户身份,而不是用Threadlocal来直接获取呢?)
详细内容
java随笔录
为什么使用Spring Security来获取用户身份,而不是用Threadlocal来直接获取呢?
首先,Spring Security是一个专门为Java应用程序提供安全服务的框架,它内置了一系列成熟的安全机制。在获取用户身份时,Spring Security会对用户的凭证进行严格的验证和加密处理,确保只有经过授权的用户才能访问应用程序。
例如,在基于OAuth 2.0的身份验证场景中,Spring Security可以处理复杂的令牌验证和用户信息提取过程,防止令牌被篡改或伪造。
同时,Spring Security提供了一套统一的配置和管理方式,通过配置文件或注解可以方便地定义不同的安全策略。当应用程序的安全需求发生变化时,只需要修改相应的配置即可,而不需要对大量的业务代码进行修改。
ThreadLocal只是一个线程级别的变量存储机制,它本身并不具备任何安全验证功能。如果直接使用ThreadLocal来存储用户身份信息,开发者需要自己实现身份验证和授权逻辑,这很容易引入安全漏洞。并且,ThreadLocal是一个独立的Java类,它与其他框架或服务的集成性较差。
网关认证

所有访问微服务的请求都要经过网关,在网关进行用户身份的认证可以将很多非法的请求拦截到微服务以外,这叫做网关认证。
1、在网关工程添加依赖
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
2、在properties文件中进行白名单配置,放行认证地址,公开访问地址
3、编写网关过滤
java
package com.xuecheng.gateway.config;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* @author Mr.M
* @version 1.0
* @description 网关认证过虑器
* @date 2022/9/27 12:10
*/
@Component
@Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {
//白名单
private static List<String> whitelist = null;
static {
//加载白名单
try (
InputStream resourceAsStream = GatewayAuthFilter.class.getResourceAsStream("/security-whitelist.properties");
) {
Properties properties = new Properties();
properties.load(resourceAsStream);
Set<String> strings = properties.stringPropertyNames();
whitelist= new ArrayList<>(strings);
} catch (Exception e) {
log.error("加载/security-whitelist.properties出错:{}",e.getMessage());
e.printStackTrace();
}
}
@Autowired
private TokenStore tokenStore;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String requestUrl = exchange.getRequest().getPath().value();
AntPathMatcher pathMatcher = new AntPathMatcher();
//白名单放行
for (String url : whitelist) {
if (pathMatcher.match(url, requestUrl)) {
return chain.filter(exchange);
}
}
//检查token是否存在
String token = getToken(exchange);
if (StringUtils.isBlank(token)) {
return buildReturnMono("没有认证",exchange);
}
//判断是否是有效的token
OAuth2AccessToken oAuth2AccessToken;
try {
oAuth2AccessToken = tokenStore.readAccessToken(token);
boolean expired = oAuth2AccessToken.isExpired();
if (expired) {
return buildReturnMono("认证令牌已过期",exchange);
}
return chain.filter(exchange);
} catch (InvalidTokenException e) {
log.info("认证令牌无效: {}", token);
return buildReturnMono("认证令牌无效",exchange);
}
}
/**
* 获取token
*/
private String getToken(ServerWebExchange exchange) {
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(tokenStr)) {
return null;
}
String token = tokenStr.split(" ")[1];
if (StringUtils.isBlank(token)) {
return null;
}
return token;
}
private Mono<Void> buildReturnMono(String error, ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
String jsonString = JSON.toJSONString(new RestErrorResponse(error));
byte[] bits = jsonString.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return 0;
}
}
AI随探录
代码随想录
组合问题2(回溯去重)
本题最终要的思想是去重。考虑到一般的去重方法在回溯中会超时,所以采用数组进行树层去重,区别于树根去重。
给定一个候选人编号的集合
candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的每个数字在每个组合中只能使用 一次 。**注意:**解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]提示:
1 <= candidates.length <= 1001 <= candidates[i] <= 501 <= target <= 30
java
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new LinkedList<>();
int[] abc = new int[10000];
private void back(int[] candidates, int target, int ids, int sum,int[] abc) {
if(sum > target)
return;
if(sum == target) {
result.add(new ArrayList(path));
return;
}
for(int i = ids; i < candidates.length; i++) {
if (sum + candidates[i] > target) break;
if(i > 0 && candidates[i - 1] == candidates[i] && abc[i - 1] == 0)
continue;
path.add(candidates[i]);
abc[i] = 1;
sum += candidates[i];
back(candidates,target,i + 1,sum,abc);
sum -= candidates[i];
abc[i] = 0;
path.removeLast();
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
back(candidates,target,0,0,abc);
return result;
}
}