完善权限认证
This commit is contained in:
parent
156955c448
commit
e9f8c2fae7
|
|
@ -1,4 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
|
|
|
|||
|
|
@ -119,7 +119,18 @@
|
|||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>eu.bitwalker</groupId>
|
||||
<artifactId>UserAgentUtils</artifactId>
|
||||
<version>1.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<version>6.0.1</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.qihang</groupId>-->
|
||||
<!-- <artifactId>oms-core</artifactId>-->
|
||||
<!-- <version>1.0</version>-->
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
package com.qihang.oms.api.config;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class JwtUtil {
|
||||
/**
|
||||
* 需要拦截的请求头信息
|
||||
*/
|
||||
public static final String TOKEN_HEADER = "token";
|
||||
/**
|
||||
* 有效期
|
||||
*/
|
||||
public static final Long JWT_EXPIRE_TIME = 60 * 60 * 1000L; // 1h
|
||||
/**
|
||||
* 加密算法
|
||||
*/
|
||||
public static final String SIGN_ALGORITHMS = "AES";
|
||||
/**
|
||||
* jwt key
|
||||
*/
|
||||
public static final String JWT_KEY = "security";
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
* @param id 唯一标识(盐值)
|
||||
* @param subject
|
||||
* @param expire
|
||||
* @return
|
||||
*/
|
||||
public static String createToken(String id, String subject, Long expire) {
|
||||
JwtBuilder builder = getJwtBuilder(subject, expire, id);
|
||||
return builder.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public static Claims parseToken(String token) {
|
||||
SecretKey secretKey = generalKey();
|
||||
Claims body = Jwts.parser()
|
||||
.setSigningKey(secretKey)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return body;
|
||||
}
|
||||
|
||||
private static JwtBuilder getJwtBuilder(String subject, Long expire, String uuid) {
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
|
||||
SecretKey secretKey = generalKey();
|
||||
Date date = new Date();
|
||||
if (expire == null) {
|
||||
expire = JWT_EXPIRE_TIME;
|
||||
}
|
||||
if (uuid == null) {
|
||||
uuid = getUUID();
|
||||
}
|
||||
Long expireTime = date.getTime() + expire;
|
||||
Date expireDate = new Date(expireTime);
|
||||
JwtBuilder builder = Jwts.builder()
|
||||
.setId(uuid) // 唯一标识
|
||||
.setSubject(subject) // 签名数据/主题
|
||||
.setIssuer(JWT_KEY) // 签发者
|
||||
.setIssuedAt(date) // 签发时间
|
||||
.signWith(signatureAlgorithm, secretKey) // 签名算法 + 秘钥
|
||||
.setExpiration(expireDate); // 过期时间
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static String getUUID() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
// 生成秘钥
|
||||
public static SecretKey generalKey() {
|
||||
byte[] encodeKey = Base64.getDecoder().decode(JWT_KEY);
|
||||
SecretKey secretKey = new SecretKeySpec(encodeKey, 0, encodeKey.length, SIGN_ALGORITHMS);
|
||||
return secretKey;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
package com.qihang.oms.api.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* spring redis 工具类
|
||||
*
|
||||
* @author qihang
|
||||
**/
|
||||
@SuppressWarnings(value = { "unchecked", "rawtypes" })
|
||||
@Component
|
||||
public class RedisCache
|
||||
{
|
||||
@Autowired
|
||||
public RedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value)
|
||||
{
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @param timeout 时间
|
||||
* @param timeUnit 时间颗粒度
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
|
||||
{
|
||||
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param timeout 超时时间
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public boolean expire(final String key, final long timeout)
|
||||
{
|
||||
return expire(key, timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param timeout 超时时间
|
||||
* @param unit 时间单位
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public boolean expire(final String key, final long timeout, final TimeUnit unit)
|
||||
{
|
||||
return redisTemplate.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @return 有效时间
|
||||
*/
|
||||
public long getExpire(final String key)
|
||||
{
|
||||
return redisTemplate.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 key是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hasKey(String key)
|
||||
{
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象。
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> T getCacheObject(final String key)
|
||||
{
|
||||
ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
return operation.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个对象
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public boolean deleteObject(final String key)
|
||||
{
|
||||
return redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除集合对象
|
||||
*
|
||||
* @param collection 多个对象
|
||||
* @return
|
||||
*/
|
||||
public boolean deleteObject(final Collection collection)
|
||||
{
|
||||
return redisTemplate.delete(collection) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存List数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param dataList 待缓存的List数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public <T> long setCacheList(final String key, final List<T> dataList)
|
||||
{
|
||||
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
|
||||
return count == null ? 0 : count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> List<T> getCacheList(final String key)
|
||||
{
|
||||
return redisTemplate.opsForList().range(key, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Set
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @param dataSet 缓存的数据
|
||||
* @return 缓存数据的对象
|
||||
*/
|
||||
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
|
||||
{
|
||||
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
|
||||
Iterator<T> it = dataSet.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
setOperation.add(it.next());
|
||||
}
|
||||
return setOperation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的set
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Set<T> getCacheSet(final String key)
|
||||
{
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Map
|
||||
*
|
||||
* @param key
|
||||
* @param dataMap
|
||||
*/
|
||||
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
|
||||
{
|
||||
if (dataMap != null) {
|
||||
redisTemplate.opsForHash().putAll(key, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的Map
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Map<String, T> getCacheMap(final String key)
|
||||
{
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 往Hash中存入数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @param value 值
|
||||
*/
|
||||
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
|
||||
{
|
||||
redisTemplate.opsForHash().put(key, hKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return Hash中的对象
|
||||
*/
|
||||
public <T> T getCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
|
||||
return opsForHash.get(key, hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKeys Hash键集合
|
||||
* @return Hash对象集合
|
||||
*/
|
||||
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
|
||||
{
|
||||
return redisTemplate.opsForHash().multiGet(key, hKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的某条数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean deleteCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
return redisTemplate.opsForHash().delete(key, hKey) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象列表
|
||||
*
|
||||
* @param pattern 字符串前缀
|
||||
* @return 对象列表
|
||||
*/
|
||||
public Collection<String> keys(final String pattern)
|
||||
{
|
||||
return redisTemplate.keys(pattern);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.qihang.oms.api.config;
|
||||
|
||||
|
||||
import com.qihang.oms.api.utils.FastJson2JsonRedisSerializer;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
package com.qihang.oms.api.config;
|
||||
|
||||
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
@Autowired
|
||||
private TokenFilter tokenFilter;
|
||||
@Autowired
|
||||
private AuthenticationExceptionHandler authenticationExceptionHandler;
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// List<String> permitAllPaths = new ArrayList<>();//security.getPermitAllPath();
|
||||
// permitAllPaths.add("/login");
|
||||
// // 配置不需要认证的请求(这里所有的路径可以写在配置文件上修改时就不用改代码)
|
||||
// if (!CollectionUtils.isEmpty(permitAllPaths)) {
|
||||
// permitAllPaths.forEach(path -> {
|
||||
// try {
|
||||
// http.authorizeHttpRequests().requestMatchers(path).permitAll();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// http.csrf().disable().authorizeHttpRequests().requestMatchers(HttpMethod.GET,"/home").permitAll();
|
||||
// 关闭csrf因为不使用session
|
||||
http.csrf().disable()
|
||||
// 不通过Session获取SecurityContext
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
|
||||
|
||||
.requestMatchers( "/home").permitAll()
|
||||
.requestMatchers("/login").permitAll()
|
||||
// 允许 SpringMVC 的默认错误地址匿名访问
|
||||
.requestMatchers("/error").permitAll()
|
||||
// 其他所有接口必须有Authority信息,Authority在登录成功后的UserDetailsImpl对象中默认设置“ROLE_USER”
|
||||
//.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
|
||||
// 允许任意请求被已登录用户访问,不检查Authority
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
// 除了上面那些请求都需要认证 https://blog.csdn.net/xieshaohu/article/details/129780439
|
||||
// .anyRequest().authenticated()
|
||||
// .and()
|
||||
// 配置异常处理
|
||||
// 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
|
||||
// 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(authenticationExceptionHandler);
|
||||
// 配置token拦截过滤器
|
||||
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份认证管理器,调用authenticate()方法完成认证
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码加密器
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
package com.qihang.oms.api.config;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.qihang.oms.api.common.R;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @Description
|
||||
* @Author
|
||||
* @Date
|
||||
**/
|
||||
@Component
|
||||
public class TokenFilter extends OncePerRequestFilter {
|
||||
private Logger log = LoggerFactory.getLogger(getClass());
|
||||
private AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
// @Autowired
|
||||
// private DemoConfiguration.Security security;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
String token = request.getHeader("token");
|
||||
log.info("intercept " + request.getRequestURI());
|
||||
|
||||
// token=1用于swagger页面调用API
|
||||
/*if (!StringUtils.hasText(token) || "1".equals(token)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}*/
|
||||
// 判断是否是放行请求
|
||||
if (isFilterRequest(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = JwtUtil.parseToken(token);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
fallback("token解析失败(非法token)!", response);
|
||||
return;
|
||||
}
|
||||
String username = claims.getSubject();
|
||||
String cache = (String) redisTemplate.opsForValue().get(String.format("login_tokens:", username));
|
||||
if (cache == null) {
|
||||
fallback("token失效,请重新登录!", response);
|
||||
return;
|
||||
}
|
||||
// SecurityUser user = JSON.parseObject(cache, SecurityUser.class);
|
||||
// log.info(JSON.toJSONString(user));
|
||||
// UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
// 放行
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void fallback(String message, HttpServletResponse response) {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
if (message == null) {
|
||||
message = "403 Forbidden";
|
||||
}
|
||||
R res = R.error(403, message);
|
||||
writer = response.getWriter();
|
||||
writer.append(JSON.toJSONString(res));
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFilterRequest(HttpServletRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
String filterPath = request.getRequestURI();
|
||||
List<String> permitAllPathList = new ArrayList<>();//security.getPermitAllPath();
|
||||
if (CollectionUtils.isEmpty(permitAllPathList)) {
|
||||
return false;
|
||||
}
|
||||
for (String path : permitAllPathList) {
|
||||
String pattern = contextPath + path;
|
||||
pattern = pattern.replaceAll("/+", "/");
|
||||
if (pathMatcher.match(pattern, filterPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,4 +27,9 @@ public class HomeController {
|
|||
public String feign() {
|
||||
return echoService.echo();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/home")
|
||||
public String home(){
|
||||
return "home";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.qihang.oms.api.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class LoginController {
|
||||
@RequestMapping(value = "/login")
|
||||
public String home(){
|
||||
return "login";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
package com.qihang.oms.api.domain;
|
||||
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户对象 sys_user
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
public class SysUser
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
private Long userId;
|
||||
|
||||
/** 部门ID */
|
||||
private Long deptId;
|
||||
|
||||
/** 用户账号 */
|
||||
|
||||
private String userName;
|
||||
|
||||
/** 用户昵称 */
|
||||
private String nickName;
|
||||
|
||||
/** 用户邮箱 */
|
||||
private String email;
|
||||
|
||||
/** 手机号码 */
|
||||
private String phonenumber;
|
||||
|
||||
/** 用户性别 */
|
||||
private String sex;
|
||||
|
||||
/** 用户头像 */
|
||||
private String avatar;
|
||||
|
||||
/** 密码 */
|
||||
private String password;
|
||||
|
||||
/** 帐号状态(0正常 1停用) */
|
||||
private String status;
|
||||
|
||||
/** 删除标志(0代表存在 2代表删除) */
|
||||
private String delFlag;
|
||||
|
||||
/** 最后登录IP */
|
||||
private String loginIp;
|
||||
|
||||
/** 最后登录时间 */
|
||||
private Date loginDate;
|
||||
|
||||
/** 角色组 */
|
||||
private Long[] roleIds;
|
||||
|
||||
/** 岗位组 */
|
||||
private Long[] postIds;
|
||||
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
public SysUser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SysUser(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public boolean isAdmin()
|
||||
{
|
||||
return isAdmin(this.userId);
|
||||
}
|
||||
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
{
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(Long deptId)
|
||||
{
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
|
||||
public String getNickName()
|
||||
{
|
||||
return nickName;
|
||||
}
|
||||
|
||||
public void setNickName(String nickName)
|
||||
{
|
||||
this.nickName = nickName;
|
||||
}
|
||||
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
|
||||
public String getEmail()
|
||||
{
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email)
|
||||
{
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
|
||||
public String getPhonenumber()
|
||||
{
|
||||
return phonenumber;
|
||||
}
|
||||
|
||||
public void setPhonenumber(String phonenumber)
|
||||
{
|
||||
this.phonenumber = phonenumber;
|
||||
}
|
||||
|
||||
public String getSex()
|
||||
{
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(String sex)
|
||||
{
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public String getAvatar()
|
||||
{
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar)
|
||||
{
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getLoginIp()
|
||||
{
|
||||
return loginIp;
|
||||
}
|
||||
|
||||
public void setLoginIp(String loginIp)
|
||||
{
|
||||
this.loginIp = loginIp;
|
||||
}
|
||||
|
||||
public Date getLoginDate()
|
||||
{
|
||||
return loginDate;
|
||||
}
|
||||
|
||||
public void setLoginDate(Date loginDate)
|
||||
{
|
||||
this.loginDate = loginDate;
|
||||
}
|
||||
|
||||
|
||||
public Long[] getRoleIds()
|
||||
{
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public void setRoleIds(Long[] roleIds)
|
||||
{
|
||||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
public Long[] getPostIds()
|
||||
{
|
||||
return postIds;
|
||||
}
|
||||
|
||||
public void setPostIds(Long[] postIds)
|
||||
{
|
||||
this.postIds = postIds;
|
||||
}
|
||||
|
||||
public Long getRoleId()
|
||||
{
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId)
|
||||
{
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.qihang.oms.api.config;
|
||||
package com.qihang.oms.api.security;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.servlet.ServletException;
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.qihang.oms.api.security;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.qihang.oms.api.common.R;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* token过滤器 验证token有效性
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
@Component
|
||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
|
||||
{
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
/**
|
||||
* 需要拦截的请求头信息
|
||||
*/
|
||||
@Value("${token.header:'Authorization'}")
|
||||
public String TOKEN_HEADER = "Authorization";
|
||||
private Logger log = LoggerFactory.getLogger(getClass());
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException
|
||||
{
|
||||
String token = request.getHeader(TOKEN_HEADER);
|
||||
log.info("intercept " + request.getRequestURI());
|
||||
log.info("token: " + token);
|
||||
LoginUser loginUser = tokenService.getLoginUser(request);
|
||||
if (loginUser !=null )
|
||||
{
|
||||
tokenService.verifyToken(loginUser);
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
|
||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void fallback(String message, HttpServletResponse response) {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
if (message == null) {
|
||||
message = "403 Forbidden";
|
||||
}
|
||||
R res = R.error(403, message);
|
||||
writer = response.getWriter();
|
||||
writer.append(JSON.toJSONString(res));
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
//package com.qihang.oms.api.security;
|
||||
//
|
||||
//import io.jsonwebtoken.Claims;
|
||||
//import io.jsonwebtoken.JwtBuilder;
|
||||
//import io.jsonwebtoken.Jwts;
|
||||
//import io.jsonwebtoken.SignatureAlgorithm;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//
|
||||
//import javax.crypto.SecretKey;
|
||||
//import javax.crypto.spec.SecretKeySpec;
|
||||
//import java.util.Base64;
|
||||
//import java.util.Date;
|
||||
//import java.util.UUID;
|
||||
//
|
||||
//public class JwtUtil {
|
||||
// /**
|
||||
// * 需要拦截的请求头信息
|
||||
// */
|
||||
// @Value("${token.header:'Authorization'}")
|
||||
// public static final String TOKEN_HEADER = "Authorization";
|
||||
// /**
|
||||
// * 有效期
|
||||
// */
|
||||
// @Value("${token.expireTime:1800000}")
|
||||
// public static final Long JWT_EXPIRE_TIME = 30 * 60 * 1000L; // 30分钟
|
||||
// /**
|
||||
// * 加密算法
|
||||
// */
|
||||
// public static final String SIGN_ALGORITHMS = "AES";
|
||||
// /**
|
||||
// * jwt key
|
||||
// */
|
||||
// @Value("${token.secret:'abcdefghijklmnopqrstuvwxyz'}")
|
||||
// public static final String JWT_KEY = "abcdefghijklmnopqrstuvwxyz";
|
||||
//
|
||||
// /**
|
||||
// * 获取token
|
||||
// * @param id 唯一标识(盐值)
|
||||
// * @param subject
|
||||
// * @param expire
|
||||
// * @return
|
||||
// */
|
||||
// public static String createToken(String id, String subject, Long expire) {
|
||||
// JwtBuilder builder = getJwtBuilder(subject, expire, id);
|
||||
// return builder.compact();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 解析token
|
||||
// * @param token
|
||||
// * @return
|
||||
// */
|
||||
// public static Claims parseToken(String token) {
|
||||
// SecretKey secretKey = generalKey();
|
||||
// Claims body = Jwts.parser()
|
||||
// .setSigningKey(secretKey)
|
||||
// .parseClaimsJws(token)
|
||||
// .getBody();
|
||||
// return body;
|
||||
// }
|
||||
//
|
||||
// private static JwtBuilder getJwtBuilder(String subject, Long expire, String uuid) {
|
||||
// SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
|
||||
// SecretKey secretKey = generalKey();
|
||||
// Date date = new Date();
|
||||
// if (expire == null) {
|
||||
// expire = JWT_EXPIRE_TIME;
|
||||
// }
|
||||
// if (uuid == null) {
|
||||
// uuid = getUUID();
|
||||
// }
|
||||
// Long expireTime = date.getTime() + expire;
|
||||
// Date expireDate = new Date(expireTime);
|
||||
// JwtBuilder builder = Jwts.builder()
|
||||
// .setId(uuid) // 唯一标识
|
||||
// .setSubject(subject) // 签名数据/主题
|
||||
// .setIssuer(JWT_KEY) // 签发者
|
||||
// .setIssuedAt(date) // 签发时间
|
||||
// .signWith(signatureAlgorithm, secretKey) // 签名算法 + 秘钥
|
||||
// .setExpiration(expireDate); // 过期时间
|
||||
// return builder;
|
||||
// }
|
||||
//
|
||||
// public static String getUUID() {
|
||||
// return UUID.randomUUID().toString();
|
||||
// }
|
||||
//
|
||||
// // 生成秘钥
|
||||
// public static SecretKey generalKey() {
|
||||
// byte[] encodeKey = Base64.getDecoder().decode(JWT_KEY);
|
||||
// SecretKey secretKey = new SecretKeySpec(encodeKey, 0, encodeKey.length, SIGN_ALGORITHMS);
|
||||
// return secretKey;
|
||||
// }
|
||||
//}
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
package com.qihang.oms.api.security;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.qihang.oms.api.domain.SysUser;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 登录用户身份权限
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
public class LoginUser implements UserDetails
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 用户唯一标识
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
private Long loginTime;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Long expireTime;
|
||||
|
||||
/**
|
||||
* 登录IP地址
|
||||
*/
|
||||
private String ipaddr;
|
||||
|
||||
/**
|
||||
* 登录地点
|
||||
*/
|
||||
private String loginLocation;
|
||||
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String os;
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private Set<String> permissions;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private SysUser user;
|
||||
|
||||
public LoginUser()
|
||||
{
|
||||
}
|
||||
|
||||
public LoginUser(SysUser user, Set<String> permissions)
|
||||
{
|
||||
this.user = user;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
|
||||
{
|
||||
this.userId = userId;
|
||||
this.deptId = deptId;
|
||||
this.user = user;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
{
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(Long deptId)
|
||||
{
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
public String getToken()
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token)
|
||||
{
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@JSONField(serialize = false)
|
||||
@Override
|
||||
public String getPassword()
|
||||
{
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername()
|
||||
{
|
||||
return user.getUserName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户是否未过期,过期无法验证
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
@Override
|
||||
public boolean isAccountNonExpired()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定用户是否解锁,锁定的用户无法进行身份验证
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
@Override
|
||||
public boolean isAccountNonLocked()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可用 ,禁用的用户不能身份验证
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
@Override
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public Long getLoginTime()
|
||||
{
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public void setLoginTime(Long loginTime)
|
||||
{
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
public String getIpaddr()
|
||||
{
|
||||
return ipaddr;
|
||||
}
|
||||
|
||||
public void setIpaddr(String ipaddr)
|
||||
{
|
||||
this.ipaddr = ipaddr;
|
||||
}
|
||||
|
||||
public String getLoginLocation()
|
||||
{
|
||||
return loginLocation;
|
||||
}
|
||||
|
||||
public void setLoginLocation(String loginLocation)
|
||||
{
|
||||
this.loginLocation = loginLocation;
|
||||
}
|
||||
|
||||
public String getBrowser()
|
||||
{
|
||||
return browser;
|
||||
}
|
||||
|
||||
public void setBrowser(String browser)
|
||||
{
|
||||
this.browser = browser;
|
||||
}
|
||||
|
||||
public String getOs()
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
public void setOs(String os)
|
||||
{
|
||||
this.os = os;
|
||||
}
|
||||
|
||||
public Long getExpireTime()
|
||||
{
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public void setExpireTime(Long expireTime)
|
||||
{
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public Set<String> getPermissions()
|
||||
{
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public void setPermissions(Set<String> permissions)
|
||||
{
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public SysUser getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(SysUser user)
|
||||
{
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.qihang.oms.api.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Autowired
|
||||
private SecurityUserDetailsService userDetailsService;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationExceptionHandler invalidAuthenticationEntryPoint;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAuthenticationTokenFilter authenticationJwtTokenFilter() {
|
||||
return new JwtAuthenticationTokenFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
http
|
||||
// 禁用basic明文验证
|
||||
.httpBasic().disable()
|
||||
// 前后端分离架构不需要csrf保护
|
||||
.csrf().disable()
|
||||
// 禁用默认登录页
|
||||
.formLogin().disable()
|
||||
// 禁用默认登出页
|
||||
.logout().disable()
|
||||
// 设置异常的EntryPoint,如果不设置,默认使用Http403ForbiddenEntryPoint
|
||||
.exceptionHandling(exceptions -> exceptions.authenticationEntryPoint(invalidAuthenticationEntryPoint))
|
||||
// 前后端分离是无状态的,不需要session了,直接禁用。
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
|
||||
// 允许所有OPTIONS请求
|
||||
.requestMatchers("/home").permitAll()
|
||||
.requestMatchers(HttpMethod.GET, "/favicon.ico").permitAll()
|
||||
// 允许直接访问授权登录接口
|
||||
.requestMatchers(HttpMethod.POST, "/login").permitAll()
|
||||
// 允许 SpringMVC 的默认错误地址匿名访问
|
||||
.requestMatchers("/error").permitAll()
|
||||
// 其他所有接口必须有Authority信息,Authority在登录成功后的UserDetailsImpl对象中默认设置“ROLE_USER”
|
||||
//.requestMatchers("/**").hasAnyAuthority("ROLE_USER")
|
||||
// 允许任意请求被已登录用户访问,不检查Authority
|
||||
.anyRequest().authenticated())
|
||||
.authenticationProvider(authenticationProvider())
|
||||
// 加我们自定义的过滤器,替代UsernamePasswordAuthenticationFilter
|
||||
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
// 调用 JwtUserDetailService实例执行实际校验
|
||||
return username -> userDetailsService.loadUserByUsername(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用loadUserByUsername获得UserDetail信息,在AbstractUserDetailsAuthenticationProvider里执行用户状态检查
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationProvider authenticationProvider() {
|
||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
// DaoAuthenticationProvider 从自定义的 userDetailsService.loadUserByUsername 方法获取UserDetails
|
||||
authProvider.setUserDetailsService(userDetailsService());
|
||||
// 设置密码编辑器
|
||||
authProvider.setPasswordEncoder(passwordEncoder());
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录时需要调用AuthenticationManager.authenticate执行一次校验
|
||||
*
|
||||
* @param config
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
||||
return config.getAuthenticationManager();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.qihang.oms.api.security;
|
||||
|
||||
import com.qihang.oms.api.domain.SysUser;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class SecurityUserDetailsService implements UserDetailsService {
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
|
||||
SysUser user = new SysUser();
|
||||
user.setUserName("admin");
|
||||
Set<String> p = new HashSet<>();
|
||||
p.add("all");
|
||||
UserDetails u = new LoginUser(1L,1L,user,p);
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
package com.qihang.oms.api.security;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.qihang.oms.api.config.RedisCache;
|
||||
import com.qihang.oms.api.utils.IdUtils;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* token验证处理
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
@Component
|
||||
public class TokenService
|
||||
{
|
||||
// 令牌自定义标识
|
||||
@Value("${token.header:'Authorization'}")
|
||||
private String header;
|
||||
|
||||
// 令牌秘钥
|
||||
@Value("${token.secret:'abcdefghijklmnopqrstuvwxyz'}")
|
||||
private String secret;
|
||||
|
||||
// 令牌有效期(默认30分钟)
|
||||
@Value("${token.expireTime:30}")
|
||||
private int expireTime;
|
||||
|
||||
protected static final long MILLIS_SECOND = 1000;
|
||||
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
|
||||
|
||||
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public LoginUser getLoginUser(HttpServletRequest request)
|
||||
{
|
||||
// 获取请求携带的令牌
|
||||
String token = getToken(request);
|
||||
if (StringUtils.isNotEmpty(token))
|
||||
{
|
||||
try
|
||||
{
|
||||
Claims claims = parseToken(token);
|
||||
// 解析对应的权限以及用户信息
|
||||
String uuid = (String) claims.get("login_user_key");
|
||||
String userKey = getTokenKey(uuid);
|
||||
LoginUser user = redisCache.getCacheObject(userKey);
|
||||
return user;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户身份信息
|
||||
*/
|
||||
public void setLoginUser(LoginUser loginUser)
|
||||
{
|
||||
if (loginUser!= null && StringUtils.isNotEmpty(loginUser.getToken()))
|
||||
{
|
||||
refreshToken(loginUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户身份信息
|
||||
*/
|
||||
public void delLoginUser(String token)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(token))
|
||||
{
|
||||
String userKey = getTokenKey(token);
|
||||
redisCache.deleteObject(userKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*
|
||||
* @param loginUser 用户信息
|
||||
* @return 令牌
|
||||
*/
|
||||
public String createToken(LoginUser loginUser)
|
||||
{
|
||||
String token = IdUtils.fastUUID();
|
||||
loginUser.setToken(token);
|
||||
setUserAgent(loginUser);
|
||||
refreshToken(loginUser);
|
||||
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("login_user_key", token);
|
||||
return createToken(claims);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌有效期,相差不足20分钟,自动刷新缓存
|
||||
*
|
||||
* @param loginUser
|
||||
* @return 令牌
|
||||
*/
|
||||
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());
|
||||
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户代理信息
|
||||
*
|
||||
* @param loginUser 登录信息
|
||||
*/
|
||||
public void setUserAgent(LoginUser loginUser)
|
||||
{
|
||||
// UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
// String ip = IpUtils.getIpAddr();
|
||||
// loginUser.setIpaddr(ip);
|
||||
// loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
|
||||
// loginUser.setBrowser(userAgent.getBrowser().getName());
|
||||
// loginUser.setOs(userAgent.getOperatingSystem().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据声明生成令牌
|
||||
*
|
||||
* @param claims 数据声明
|
||||
* @return 令牌
|
||||
*/
|
||||
private String createToken(Map<String, Object> claims)
|
||||
{
|
||||
String token = Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.signWith(SignatureAlgorithm.HS512, secret).compact();
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取数据声明
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 数据声明
|
||||
*/
|
||||
private Claims parseToken(String token)
|
||||
{
|
||||
return Jwts.parser()
|
||||
.setSigningKey(secret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取用户名
|
||||
*
|
||||
* @param token 令牌
|
||||
* @return 用户名
|
||||
*/
|
||||
public String getUsernameFromToken(String token)
|
||||
{
|
||||
Claims claims = parseToken(token);
|
||||
return claims.getSubject();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*
|
||||
* @param request
|
||||
* @return token
|
||||
*/
|
||||
private String getToken(HttpServletRequest request)
|
||||
{
|
||||
String token = request.getHeader(header);
|
||||
// if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
|
||||
// {
|
||||
// token = token.replace(Constants.TOKEN_PREFIX, "");
|
||||
// }
|
||||
return token;
|
||||
}
|
||||
|
||||
private String getTokenKey(String uuid)
|
||||
{
|
||||
// return CacheConstants.LOGIN_TOKEN_KEY + uuid;
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.qihang.oms.api.config;
|
||||
package com.qihang.oms.api.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.qihang.oms.api.utils;
|
||||
|
||||
/**
|
||||
* ID生成器工具类
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
public class IdUtils
|
||||
{
|
||||
/**
|
||||
* 获取随机UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String randomUUID()
|
||||
{
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String simpleUUID()
|
||||
{
|
||||
return UUID.randomUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String fastUUID()
|
||||
{
|
||||
return UUID.fastUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String fastSimpleUUID()
|
||||
{
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
package com.qihang.oms.api.utils;
|
||||
|
||||
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
|
||||
*
|
||||
* @author qihang
|
||||
*/
|
||||
public final class UUID implements java.io.Serializable, Comparable<UUID>
|
||||
{
|
||||
private static final long serialVersionUID = -1185015143654744140L;
|
||||
|
||||
/**
|
||||
* SecureRandom 的单例
|
||||
*
|
||||
*/
|
||||
private static class Holder
|
||||
{
|
||||
static final SecureRandom numberGenerator;
|
||||
|
||||
static {
|
||||
try {
|
||||
numberGenerator = getSecureRandom();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 此UUID的最高64有效位 */
|
||||
private final long mostSigBits;
|
||||
|
||||
/** 此UUID的最低64有效位 */
|
||||
private final long leastSigBits;
|
||||
|
||||
/**
|
||||
* 私有构造
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
private UUID(byte[] data)
|
||||
{
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
for (int i = 8; i < 16; i++)
|
||||
{
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的数据构造新的 UUID。
|
||||
*
|
||||
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
|
||||
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
|
||||
*/
|
||||
public UUID(long mostSigBits, long leastSigBits)
|
||||
{
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID fastUUID()
|
||||
{
|
||||
return randomUUID(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID()
|
||||
{
|
||||
return randomUUID(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID(boolean isSecure)
|
||||
{
|
||||
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
ng.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(randomBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
|
||||
*
|
||||
* @param name 用于构造 UUID 的字节数组。
|
||||
*
|
||||
* @return 根据指定数组生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID nameUUIDFromBytes(byte[] name)
|
||||
{
|
||||
MessageDigest md;
|
||||
try
|
||||
{
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
}
|
||||
catch (NoSuchAlgorithmException nsae)
|
||||
{
|
||||
throw new InternalError("MD5 not supported");
|
||||
}
|
||||
byte[] md5Bytes = md.digest(name);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||
*
|
||||
* @param name 指定 {@code UUID} 字符串
|
||||
* @return 具有指定值的 {@code UUID}
|
||||
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
|
||||
*
|
||||
*/
|
||||
public static UUID fromString(String name)
|
||||
{
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5)
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||
}
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
components[i] = "0x" + components[i];
|
||||
}
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new UUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*/
|
||||
public long getLeastSignificantBits()
|
||||
{
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最高有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中最高有效 64 位。
|
||||
*/
|
||||
public long getMostSignificantBits()
|
||||
{
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
|
||||
* <p>
|
||||
* 版本号具有以下含意:
|
||||
* <ul>
|
||||
* <li>1 基于时间的 UUID
|
||||
* <li>2 DCE 安全 UUID
|
||||
* <li>3 基于名称的 UUID
|
||||
* <li>4 随机生成的 UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 的版本号
|
||||
*/
|
||||
public int version()
|
||||
{
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
|
||||
* <p>
|
||||
* 变体号具有以下含意:
|
||||
* <ul>
|
||||
* <li>0 为 NCS 向后兼容保留
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
|
||||
* <li>6 保留,微软向后兼容
|
||||
* <li>7 保留供以后定义使用
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 相关联的变体号
|
||||
*/
|
||||
public int variant()
|
||||
{
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时间戳值。
|
||||
*
|
||||
* <p>
|
||||
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
|
||||
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
|
||||
*
|
||||
* <p>
|
||||
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
|
||||
*/
|
||||
public long timestamp() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return (mostSigBits & 0x0FFFL) << 48//
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时钟序列值。
|
||||
*
|
||||
* <p>
|
||||
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
|
||||
* <p>
|
||||
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
|
||||
* UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的时钟序列
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public int clockSequence() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关的节点值。
|
||||
*
|
||||
* <p>
|
||||
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
|
||||
* <p>
|
||||
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的节点值
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public long node() throws UnsupportedOperationException
|
||||
{
|
||||
checkTimeBase();
|
||||
return leastSigBits & 0x0000FFFFFFFFFFFFL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
* @see #toString(boolean)
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
*/
|
||||
public String toString(boolean isSimple)
|
||||
{
|
||||
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
|
||||
// time_low
|
||||
builder.append(digits(mostSigBits >> 32, 8));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// time_mid
|
||||
builder.append(digits(mostSigBits >> 16, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// time_high_and_version
|
||||
builder.append(digits(mostSigBits, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// variant_and_sequence
|
||||
builder.append(digits(leastSigBits >> 48, 4));
|
||||
if (!isSimple)
|
||||
{
|
||||
builder.append('-');
|
||||
}
|
||||
// node
|
||||
builder.append(digits(leastSigBits, 12));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的哈希码。
|
||||
*
|
||||
* @return UUID 的哈希码值。
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int) (hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将此对象与指定对象比较。
|
||||
* <p>
|
||||
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
|
||||
*
|
||||
* @param obj 要与之比较的对象
|
||||
*
|
||||
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if ((null == obj) || (obj.getClass() != UUID.class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UUID id = (UUID) obj;
|
||||
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
|
||||
/**
|
||||
* 将此 UUID 与指定的 UUID 比较。
|
||||
*
|
||||
* <p>
|
||||
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
|
||||
*
|
||||
* @param val 与此 UUID 比较的 UUID
|
||||
*
|
||||
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(UUID val)
|
||||
{
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||
(this.leastSigBits > val.leastSigBits ? 1 : //
|
||||
0))));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Private method start
|
||||
/**
|
||||
* 返回指定数字对应的hex值
|
||||
*
|
||||
* @param val 值
|
||||
* @param digits 位
|
||||
* @return 值
|
||||
*/
|
||||
private static String digits(long val, int digits)
|
||||
{
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为time-based版本UUID
|
||||
*/
|
||||
private void checkTimeBase()
|
||||
{
|
||||
if (version() != 1)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||
*
|
||||
* @return {@link SecureRandom}
|
||||
*/
|
||||
public static SecureRandom getSecureRandom() throws Exception {
|
||||
try
|
||||
{
|
||||
return SecureRandom.getInstance("SHA1PRNG");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数生成器对象<br>
|
||||
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
*
|
||||
* @return {@link ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom getRandom()
|
||||
{
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
}
|
||||
|
|
@ -47,9 +47,7 @@
|
|||
// // 基于token,所以不需要session
|
||||
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
// // 过滤请求
|
||||
// .authorizeRequests(authz -> authz
|
||||
// .requestMatchers(URL_WHITELIST).permitAll() // 允许访问无需认证的路径
|
||||
// .anyRequest().authenticated())
|
||||
// .authorizeRequests()
|
||||
// .formLogin(form -> form.
|
||||
// loginProcessingUrl("/login")
|
||||
// .usernameParameter("username")
|
||||
|
|
|
|||
|
|
@ -1,84 +1,84 @@
|
|||
package com.qihang.core.config;
|
||||
|
||||
import com.qihang.core.security.AuthenticationExceptionHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description SecurityConfiguration
|
||||
* @Author
|
||||
* @Date
|
||||
**/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
@Autowired
|
||||
private AuthenticationExceptionHandler authenticationExceptionHandler;
|
||||
@Autowired
|
||||
private TokenFilter tokenFilter;
|
||||
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
List<String> permitAllPaths = new ArrayList<>();//security.getPermitAllPath();
|
||||
// 配置不需要认证的请求(这里所有的路径可以写在配置文件上修改时就不用改代码)
|
||||
if (!CollectionUtils.isEmpty(permitAllPaths)) {
|
||||
permitAllPaths.forEach(path -> {
|
||||
try {
|
||||
http.authorizeHttpRequests().requestMatchers(path).permitAll();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
// 关闭csrf因为不使用session
|
||||
http.csrf().disable()
|
||||
// 不通过Session获取SecurityContext
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeHttpRequests()
|
||||
// 除了上面那些请求都需要认证
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
// 配置异常处理
|
||||
// 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
|
||||
// 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(authenticationExceptionHandler);
|
||||
// 配置token拦截过滤器
|
||||
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份认证管理器,调用authenticate()方法完成认证
|
||||
*/
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码加密器
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
}
|
||||
//package com.qihang.core.config;
|
||||
//
|
||||
//import com.qihang.core.security.AuthenticationExceptionHandler;
|
||||
//import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.security.authentication.AuthenticationManager;
|
||||
//import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
//import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
//import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
//import org.springframework.security.web.SecurityFilterChain;
|
||||
//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
//import org.springframework.util.CollectionUtils;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// *
|
||||
// * @Description SecurityConfiguration
|
||||
// * @Author
|
||||
// * @Date
|
||||
// **/
|
||||
//@Configuration
|
||||
//@EnableWebSecurity
|
||||
//public class SecurityConfiguration {
|
||||
// @Autowired
|
||||
// private AuthenticationExceptionHandler authenticationExceptionHandler;
|
||||
// @Autowired
|
||||
// private TokenFilter tokenFilter;
|
||||
//
|
||||
//
|
||||
// @Bean
|
||||
// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// List<String> permitAllPaths = new ArrayList<>();//security.getPermitAllPath();
|
||||
// // 配置不需要认证的请求(这里所有的路径可以写在配置文件上修改时就不用改代码)
|
||||
// if (!CollectionUtils.isEmpty(permitAllPaths)) {
|
||||
// permitAllPaths.forEach(path -> {
|
||||
// try {
|
||||
// http.authorizeHttpRequests().requestMatchers(path).permitAll();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// // 关闭csrf因为不使用session
|
||||
// http.csrf().disable()
|
||||
// // 不通过Session获取SecurityContext
|
||||
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
// .and()
|
||||
// .authorizeHttpRequests()
|
||||
// // 除了上面那些请求都需要认证
|
||||
// .anyRequest().authenticated()
|
||||
// .and()
|
||||
// // 配置异常处理
|
||||
// // 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
|
||||
// // 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
|
||||
// .exceptionHandling()
|
||||
// .authenticationEntryPoint(authenticationExceptionHandler);
|
||||
// // 配置token拦截过滤器
|
||||
// http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// return http.build();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 身份认证管理器,调用authenticate()方法完成认证
|
||||
// */
|
||||
// @Bean
|
||||
// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||
// return authenticationConfiguration.getAuthenticationManager();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 密码加密器
|
||||
// */
|
||||
// @Bean
|
||||
// public PasswordEncoder passwordEncoder() {
|
||||
// return new BCryptPasswordEncoder();
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
|
@ -1,122 +1,122 @@
|
|||
package com.qihang.core.config;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.qihang.core.constant.CacheConstants;
|
||||
import com.qihang.core.security.SecurityUser;
|
||||
import com.qihang.core.utils.JwtUtil;
|
||||
import com.qihang.core.utils.R;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @Description
|
||||
* @Author
|
||||
* @Date
|
||||
**/
|
||||
@Component
|
||||
public class TokenFilter extends OncePerRequestFilter {
|
||||
private Logger log = LoggerFactory.getLogger(getClass());
|
||||
private AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
//package com.qihang.core.config;
|
||||
//
|
||||
//
|
||||
//import com.alibaba.fastjson2.JSON;
|
||||
//import com.qihang.core.constant.CacheConstants;
|
||||
//import com.qihang.core.security.SecurityUser;
|
||||
//import com.qihang.core.utils.JwtUtil;
|
||||
//import com.qihang.core.utils.R;
|
||||
//import io.jsonwebtoken.Claims;
|
||||
//import jakarta.servlet.FilterChain;
|
||||
//import jakarta.servlet.ServletException;
|
||||
//import jakarta.servlet.http.HttpServletRequest;
|
||||
//import jakarta.servlet.http.HttpServletResponse;
|
||||
//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.http.MediaType;
|
||||
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
//import org.springframework.security.core.context.SecurityContextHolder;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//import org.springframework.util.AntPathMatcher;
|
||||
//import org.springframework.util.CollectionUtils;
|
||||
//import org.springframework.web.filter.OncePerRequestFilter;
|
||||
//
|
||||
//import java.io.IOException;
|
||||
//import java.io.PrintWriter;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// *
|
||||
// *
|
||||
// * @Description
|
||||
// * @Author
|
||||
// * @Date
|
||||
// **/
|
||||
//@Component
|
||||
//public class TokenFilter extends OncePerRequestFilter {
|
||||
// private Logger log = LoggerFactory.getLogger(getClass());
|
||||
// private AntPathMatcher pathMatcher = new AntPathMatcher();
|
||||
//
|
||||
// @Autowired
|
||||
// private DemoConfiguration.Security security;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
String token = request.getHeader(JwtUtil.TOKEN_HEADER);
|
||||
log.info("intercept " + request.getRequestURI());
|
||||
|
||||
// token=1用于swagger页面调用API
|
||||
/*if (!StringUtils.hasText(token) || "1".equals(token)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}*/
|
||||
// 判断是否是放行请求
|
||||
if (isFilterRequest(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = JwtUtil.parseToken(token);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
fallback("token解析失败(非法token)!", response);
|
||||
return;
|
||||
}
|
||||
String username = claims.getSubject();
|
||||
String cache = (String) redisTemplate.opsForValue().get(String.format(CacheConstants.LOGIN_TOKEN_KEY, username));
|
||||
if (cache == null) {
|
||||
fallback("token失效,请重新登录!", response);
|
||||
return;
|
||||
}
|
||||
SecurityUser user = JSON.parseObject(cache, SecurityUser.class);
|
||||
log.info(JSON.toJSONString(user));
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, null);
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
// 放行
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private void fallback(String message, HttpServletResponse response) {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
if (message == null) {
|
||||
message = "403 Forbidden";
|
||||
}
|
||||
R res = R.error(403, message);
|
||||
writer = response.getWriter();
|
||||
writer.append(JSON.toJSONString(res));
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFilterRequest(HttpServletRequest request) {
|
||||
String contextPath = request.getContextPath();
|
||||
String filterPath = request.getRequestURI();
|
||||
List<String> permitAllPathList = new ArrayList<>();//security.getPermitAllPath();
|
||||
if (CollectionUtils.isEmpty(permitAllPathList)) {
|
||||
return false;
|
||||
}
|
||||
for (String path : permitAllPathList) {
|
||||
String pattern = contextPath + path;
|
||||
pattern = pattern.replaceAll("/+", "/");
|
||||
if (pathMatcher.match(pattern, filterPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// private RedisTemplate<String, Object> redisTemplate;
|
||||
//
|
||||
//// @Autowired
|
||||
//// private DemoConfiguration.Security security;
|
||||
//
|
||||
// @Override
|
||||
// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
// String token = request.getHeader(JwtUtil.TOKEN_HEADER);
|
||||
// log.info("intercept " + request.getRequestURI());
|
||||
//
|
||||
// // token=1用于swagger页面调用API
|
||||
// /*if (!StringUtils.hasText(token) || "1".equals(token)) {
|
||||
// filterChain.doFilter(request, response);
|
||||
// return;
|
||||
// }*/
|
||||
// // 判断是否是放行请求
|
||||
// if (isFilterRequest(request)) {
|
||||
// filterChain.doFilter(request, response);
|
||||
// return;
|
||||
// }
|
||||
// Claims claims = null;
|
||||
// try {
|
||||
// claims = JwtUtil.parseToken(token);
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// fallback("token解析失败(非法token)!", response);
|
||||
// return;
|
||||
// }
|
||||
// String username = claims.getSubject();
|
||||
// String cache = (String) redisTemplate.opsForValue().get(String.format(CacheConstants.LOGIN_TOKEN_KEY, username));
|
||||
// if (cache == null) {
|
||||
// fallback("token失效,请重新登录!", response);
|
||||
// return;
|
||||
// }
|
||||
// SecurityUser user = JSON.parseObject(cache, SecurityUser.class);
|
||||
// log.info(JSON.toJSONString(user));
|
||||
// UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, null, null);
|
||||
// SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
// // 放行
|
||||
// filterChain.doFilter(request, response);
|
||||
// }
|
||||
//
|
||||
// private void fallback(String message, HttpServletResponse response) {
|
||||
// response.setCharacterEncoding("UTF-8");
|
||||
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
// PrintWriter writer = null;
|
||||
// try {
|
||||
// if (message == null) {
|
||||
// message = "403 Forbidden";
|
||||
// }
|
||||
// R res = R.error(403, message);
|
||||
// writer = response.getWriter();
|
||||
// writer.append(JSON.toJSONString(res));
|
||||
// } catch (IOException e) {
|
||||
// log.error(e.getMessage());
|
||||
// } finally {
|
||||
// if (writer != null) {
|
||||
// writer.close();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private boolean isFilterRequest(HttpServletRequest request) {
|
||||
// String contextPath = request.getContextPath();
|
||||
// String filterPath = request.getRequestURI();
|
||||
// List<String> permitAllPathList = new ArrayList<>();//security.getPermitAllPath();
|
||||
// if (CollectionUtils.isEmpty(permitAllPathList)) {
|
||||
// return false;
|
||||
// }
|
||||
// for (String path : permitAllPathList) {
|
||||
// String pattern = contextPath + path;
|
||||
// pattern = pattern.replaceAll("/+", "/");
|
||||
// if (pathMatcher.match(pattern, filterPath)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
Loading…
Reference in New Issue