mirror of
https://gitee.com/y_project/RuoYi-Cloud.git
synced 2026-01-28 04:31:57 +08:00
移除 OAuth2 改为 Redis
This commit is contained in:
@@ -8,12 +8,27 @@ package com.ruoyi.common.core.constant;
|
||||
public class CacheConstants
|
||||
{
|
||||
/**
|
||||
* oauth 缓存前缀
|
||||
* 令牌自定义标识
|
||||
*/
|
||||
public static final String OAUTH_ACCESS = "oauth:access:";
|
||||
public static final String HEADER = "Authorization";
|
||||
|
||||
/**
|
||||
* oauth 客户端信息
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String CLIENT_DETAILS_KEY = "oauth:client:details";
|
||||
public static final String TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
/**
|
||||
* 权限缓存前缀
|
||||
*/
|
||||
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
|
||||
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
public static final String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 用户名字段
|
||||
*/
|
||||
public static final String DETAILS_USERNAME = "username";
|
||||
}
|
||||
|
||||
@@ -85,7 +85,12 @@ public class Constants
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
public static final Integer CAPTCHA_EXPIRATION = 2;
|
||||
public static final long CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
/**
|
||||
* 令牌有效期(分钟)
|
||||
*/
|
||||
public final static long TOKEN_EXPIRE = 30;
|
||||
|
||||
/**
|
||||
* 参数管理 cache key
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.ruoyi.common.core.constant;
|
||||
|
||||
/**
|
||||
* 权限相关通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SecurityConstants
|
||||
{
|
||||
/**
|
||||
* 令牌类型
|
||||
*/
|
||||
public static final String BEARER_TOKEN_TYPE = "Bearer";
|
||||
|
||||
/**
|
||||
* 授权token url
|
||||
*/
|
||||
public static final String AUTH_TOKEN = "/oauth/token";
|
||||
|
||||
/**
|
||||
* 注销token url
|
||||
*/
|
||||
public static final String TOKEN_LOGOUT = "/token/logout";
|
||||
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
public static final String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 用户名字段
|
||||
*/
|
||||
public static final String DETAILS_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* sys_oauth_client_details 表的字段,不包括client_id、client_secret
|
||||
*/
|
||||
public static final String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
|
||||
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
|
||||
+ "refresh_token_validity, additional_information, autoapprove";
|
||||
|
||||
/**
|
||||
* JdbcClientDetailsService 查询语句
|
||||
*/
|
||||
public static final String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
|
||||
|
||||
/**
|
||||
* 按条件client_id 查询
|
||||
*/
|
||||
public static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
|
||||
|
||||
/**
|
||||
* 默认的查询语句
|
||||
*/
|
||||
public static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
|
||||
}
|
||||
@@ -56,5 +56,20 @@ public class UserConstants
|
||||
|
||||
/** 校验返回结果码 */
|
||||
public final static String UNIQUE = "0";
|
||||
|
||||
public final static String NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 用户名长度限制
|
||||
*/
|
||||
public static final int USERNAME_MIN_LENGTH = 2;
|
||||
|
||||
public static final int USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
/**
|
||||
* 密码长度限制
|
||||
*/
|
||||
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||
|
||||
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.ruoyi.common.core.exception;
|
||||
|
||||
/**
|
||||
* 权限异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PreAuthorizeException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PreAuthorizeException()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@ import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
import com.ruoyi.common.datascope.annotation.DataScope;
|
||||
import com.ruoyi.common.datascope.service.AwaitUserService;
|
||||
import com.ruoyi.common.security.service.TokenService;
|
||||
import com.ruoyi.system.api.domain.SysRole;
|
||||
import com.ruoyi.system.api.domain.SysUser;
|
||||
import com.ruoyi.system.api.model.UserInfo;
|
||||
import com.ruoyi.system.api.model.LoginUser;
|
||||
|
||||
/**
|
||||
* 数据过滤处理
|
||||
@@ -57,7 +57,7 @@ public class DataScopeAspect
|
||||
public static final String DATA_SCOPE = "dataScope";
|
||||
|
||||
@Autowired
|
||||
private AwaitUserService awaitUserService;
|
||||
private TokenService tokenService;
|
||||
|
||||
// 配置织入点
|
||||
@Pointcut("@annotation(com.ruoyi.common.datascope.annotation.DataScope)")
|
||||
@@ -80,7 +80,7 @@ public class DataScopeAspect
|
||||
return;
|
||||
}
|
||||
// 获取当前的用户
|
||||
UserInfo loginUser = awaitUserService.info();
|
||||
LoginUser loginUser = tokenService.getLoginUser();
|
||||
SysUser currentUser = loginUser.getSysUser();
|
||||
if (currentUser != null)
|
||||
{
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ruoyi.common.datascope.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.security.utils.SecurityUtils;
|
||||
import com.ruoyi.system.api.RemoteUserService;
|
||||
import com.ruoyi.system.api.model.UserInfo;
|
||||
|
||||
/**
|
||||
* 同步调用用户服务
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class AwaitUserService
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(AwaitUserService.class);
|
||||
|
||||
@Autowired
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 查询当前用户信息
|
||||
*
|
||||
* @return 用户基本信息
|
||||
*/
|
||||
public UserInfo info()
|
||||
{
|
||||
String username = SecurityUtils.getUsername();
|
||||
R<UserInfo> userResult = remoteUserService.getUserInfo(username);
|
||||
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
|
||||
{
|
||||
log.info("数据权限范围查询用户:{} 不存在.", username);
|
||||
return null;
|
||||
}
|
||||
return userResult.getData();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.ruoyi.common.datascope.service.AwaitUserService,\
|
||||
com.ruoyi.common.datascope.aspect.DataScopeAspect
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ruoyi.common.log.aspect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
@@ -17,16 +16,14 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.ruoyi.common.core.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.utils.ServletUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.utils.ip.IpUtils;
|
||||
import com.ruoyi.common.log.annotation.Log;
|
||||
import com.ruoyi.common.log.enums.BusinessStatus;
|
||||
import com.ruoyi.common.log.service.AsyncLogService;
|
||||
import com.ruoyi.common.security.domain.LoginUser;
|
||||
import com.ruoyi.common.security.utils.SecurityUtils;
|
||||
import com.ruoyi.system.api.domain.SysOperLog;
|
||||
|
||||
/**
|
||||
@@ -83,9 +80,6 @@ public class LogAspect
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前的用户
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
|
||||
// *========数据库日志=========*//
|
||||
SysOperLog operLog = new SysOperLog();
|
||||
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
||||
@@ -96,9 +90,11 @@ public class LogAspect
|
||||
operLog.setJsonResult(JSON.toJSONString(jsonResult));
|
||||
|
||||
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
|
||||
if (loginUser != null)
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
String username = request.getHeader(CacheConstants.DETAILS_USERNAME);
|
||||
if (StringUtils.isNotBlank(username))
|
||||
{
|
||||
operLog.setOperName(loginUser.getUsername());
|
||||
operLog.setOperName(username);
|
||||
}
|
||||
|
||||
if (e != null)
|
||||
@@ -163,11 +159,6 @@ public class LogAspect
|
||||
String params = argsArrayToString(joinPoint.getArgs());
|
||||
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||
}
|
||||
else
|
||||
{
|
||||
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ public class RedisService
|
||||
* @param timeout 时间
|
||||
* @param timeUnit 时间颗粒度
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
|
||||
public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
|
||||
{
|
||||
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
|
||||
}
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spring Security Oauth2 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-oauth2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RuoYi Api System -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RuoYi Common Redis-->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -5,9 +5,7 @@ import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import com.ruoyi.common.security.feign.OAuth2FeignConfig;
|
||||
import com.ruoyi.common.security.config.ApplicationConfig;
|
||||
import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -20,7 +18,7 @@ import com.ruoyi.common.security.config.SecurityImportBeanDefinitionRegistrar;
|
||||
// 开启线程异步执行
|
||||
@EnableAsync
|
||||
// 自动加载类
|
||||
@Import({ SecurityImportBeanDefinitionRegistrar.class, OAuth2FeignConfig.class, ApplicationConfig.class })
|
||||
@Import({ApplicationConfig.class})
|
||||
public @interface EnableCustomConfig
|
||||
{
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.ruoyi.common.security.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 权限注解
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PreAuthorize
|
||||
{
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*/
|
||||
public String hasPermi() default "";
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
|
||||
*/
|
||||
public String lacksPermi() default "";
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限
|
||||
*/
|
||||
public String[] hasAnyPermi() default {};
|
||||
|
||||
/**
|
||||
* 判断用户是否拥有某个角色
|
||||
*/
|
||||
public String hasRole() default "";
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某角色,与 isRole逻辑相反
|
||||
*/
|
||||
public String lacksRole() default "";
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色
|
||||
*/
|
||||
public String[] hasAnyRoles() default {};
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.ruoyi.common.security.aspect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.common.core.exception.PreAuthorizeException;
|
||||
import com.ruoyi.common.security.annotation.PreAuthorize;
|
||||
import com.ruoyi.common.security.service.TokenService;
|
||||
import com.ruoyi.system.api.model.LoginUser;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class PreAuthorizeAspect
|
||||
{
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/** 所有权限标识 */
|
||||
private static final String ALL_PERMISSION = "*:*:*";
|
||||
|
||||
/** 管理员角色权限标识 */
|
||||
private static final String SUPER_ADMIN = "admin";
|
||||
|
||||
@Around("@annotation(com.ruoyi.common.security.annotation.PreAuthorize)")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable
|
||||
{
|
||||
Signature signature = point.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
Method method = methodSignature.getMethod();
|
||||
PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
|
||||
if (annotation == null)
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(annotation.hasPermi()) && hasPermi(annotation.hasPermi()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else if (StringUtils.isEmpty(annotation.lacksPermi()) && hasPermi(annotation.lacksPermi()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else if (StringUtils.isEmpty(annotation.hasAnyPermi()) && hasAnyPermi(annotation.hasAnyPermi()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else if (StringUtils.isEmpty(annotation.hasRole()) && hasRole(annotation.hasRole()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else if (StringUtils.isEmpty(annotation.lacksRole()) && lacksRole(annotation.lacksRole()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else if (StringUtils.isEmpty(annotation.hasAnyRoles()) && hasAnyRoles(annotation.hasAnyRoles()))
|
||||
{
|
||||
return point.proceed();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PreAuthorizeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermi(String permission)
|
||||
{
|
||||
LoginUser userInfo = tokenService.getLoginUser();
|
||||
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return hasPermissions(userInfo.getPermissions(), permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否不具备某权限
|
||||
*/
|
||||
public boolean lacksPermi(String permission)
|
||||
{
|
||||
return hasPermi(permission) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public boolean hasAnyPermi(String[] permissions)
|
||||
{
|
||||
LoginUser userInfo = tokenService.getLoginUser();
|
||||
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getPermissions()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Collection<String> authorities = userInfo.getPermissions();
|
||||
for (String permission : permissions)
|
||||
{
|
||||
if (permission != null && hasPermissions(authorities, permission))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否拥有某个角色
|
||||
*
|
||||
* @param role 角色字符串
|
||||
* @return 用户是否具备某角色
|
||||
*/
|
||||
public boolean hasRole(String role)
|
||||
{
|
||||
LoginUser userInfo = tokenService.getLoginUser();
|
||||
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String roleKey : userInfo.getRoles())
|
||||
{
|
||||
if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(role))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某角色,与 isRole逻辑相反。
|
||||
*
|
||||
* @param role 角色名称
|
||||
* @return 用户是否不具备某角色
|
||||
*/
|
||||
public boolean lacksRole(String role)
|
||||
{
|
||||
return hasRole(role) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色
|
||||
*
|
||||
* @param roles 角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public boolean hasAnyRoles(String[] roles)
|
||||
{
|
||||
LoginUser userInfo = tokenService.getLoginUser();
|
||||
if (StringUtils.isEmpty(userInfo) || CollectionUtils.isEmpty(userInfo.getRoles()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String role : roles)
|
||||
{
|
||||
if (hasRole(role))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param authorities 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
private boolean hasPermissions(Collection<String> authorities, String permission)
|
||||
{
|
||||
return authorities.stream().filter(StringUtils::hasText)
|
||||
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.ruoyi.common.security.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 忽略服务间的认证
|
||||
*
|
||||
* @author ruoyi
|
||||
**/
|
||||
@Component
|
||||
@Configurable
|
||||
@ConfigurationProperties(prefix = "security.oauth2.ignore")
|
||||
public class AuthIgnoreConfig
|
||||
{
|
||||
private List<String> urls = new ArrayList<>();
|
||||
|
||||
public List<String> getUrls()
|
||||
{
|
||||
return urls;
|
||||
}
|
||||
|
||||
public void setUrls(List<String> urls)
|
||||
{
|
||||
this.urls = urls;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.ruoyi.common.security.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.security.domain.LoginUser;
|
||||
|
||||
/**
|
||||
* https://my.oschina.net/giegie/blog/3023768 根据checktoken 的结果转化用户信息
|
||||
*
|
||||
* @author lengleng
|
||||
*/
|
||||
public class CommonUserConverter implements UserAuthenticationConverter
|
||||
{
|
||||
private static final String N_A = "N/A";
|
||||
|
||||
/**
|
||||
* 将授权信息返回到资源服务
|
||||
*/
|
||||
@Override
|
||||
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication)
|
||||
{
|
||||
Map<String, Object> authMap = new LinkedHashMap<>();
|
||||
authMap.put(USERNAME, userAuthentication.getName());
|
||||
if (userAuthentication.getAuthorities() != null && !userAuthentication.getAuthorities().isEmpty())
|
||||
{
|
||||
authMap.put(AUTHORITIES, AuthorityUtils.authorityListToSet(userAuthentication.getAuthorities()));
|
||||
}
|
||||
return authMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户认证信息
|
||||
*/
|
||||
@Override
|
||||
public Authentication extractAuthentication(Map<String, ?> map)
|
||||
{
|
||||
if (map.containsKey(USERNAME))
|
||||
{
|
||||
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
|
||||
|
||||
Long userId = Convert.toLong(map.get(SecurityConstants.DETAILS_USER_ID));
|
||||
String username = (String) map.get(SecurityConstants.DETAILS_USERNAME);
|
||||
LoginUser user = new LoginUser(userId, username, N_A, true, true, true, true, authorities);
|
||||
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限资源信息
|
||||
*/
|
||||
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map)
|
||||
{
|
||||
Object authorities = map.get(AUTHORITIES);
|
||||
if (authorities instanceof String)
|
||||
{
|
||||
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
|
||||
}
|
||||
if (authorities instanceof Collection)
|
||||
{
|
||||
return AuthorityUtils.commaSeparatedStringToAuthorityList(
|
||||
StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
|
||||
}
|
||||
throw new IllegalArgumentException("Authorities must be either a String or a Collection");
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.ruoyi.common.security.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
|
||||
/**
|
||||
*
|
||||
* @EnableGlobalMethodSecurity(securedEnabled=true)
|
||||
* 开启@Secured 注解过滤权限
|
||||
*
|
||||
* @EnableGlobalMethodSecurity(jsr250Enabled=true)
|
||||
* 开启@RolesAllowed 注解过滤权限
|
||||
*
|
||||
* @EnableGlobalMethodSecurity(prePostEnabled=true)
|
||||
* 使用表达式时间方法级别的安全性 4个注解可用
|
||||
* -@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
|
||||
* -@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
|
||||
* -@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
|
||||
* -@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class MethodSecurityConfig
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.ruoyi.common.security.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* oauth2 服务配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
@EnableResourceServer
|
||||
public class ResourceServerConfig extends ResourceServerConfigurerAdapter
|
||||
{
|
||||
@Autowired
|
||||
private ResourceServerProperties resourceServerProperties;
|
||||
|
||||
@Autowired
|
||||
private OAuth2ClientProperties oAuth2ClientProperties;
|
||||
|
||||
@Bean
|
||||
public AuthIgnoreConfig authIgnoreConfig()
|
||||
{
|
||||
return new AuthIgnoreConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
public RestTemplate restTemplate()
|
||||
{
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceServerTokenServices tokenServices()
|
||||
{
|
||||
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
|
||||
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
|
||||
UserAuthenticationConverter userTokenConverter = new CommonUserConverter();
|
||||
accessTokenConverter.setUserTokenConverter(userTokenConverter);
|
||||
remoteTokenServices.setCheckTokenEndpointUrl(resourceServerProperties.getTokenInfoUri());
|
||||
remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId());
|
||||
remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret());
|
||||
remoteTokenServices.setRestTemplate(restTemplate());
|
||||
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
|
||||
return remoteTokenServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception
|
||||
{
|
||||
http.csrf().disable();
|
||||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
|
||||
.authorizeRequests();
|
||||
// 不登录可以访问
|
||||
authIgnoreConfig().getUrls().forEach(url -> registry.antMatchers(url).permitAll());
|
||||
registry.anyRequest().authenticated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ResourceServerSecurityConfigurer resources)
|
||||
{
|
||||
resources.tokenServices(tokenServices());
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.ruoyi.common.security.config;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 导入 SecurityImportBeanDefinitionRegistrar 自动加载类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SecurityImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar
|
||||
{
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
|
||||
{
|
||||
Class<ResourceServerConfig> aClass = ResourceServerConfig.class;
|
||||
String beanName = StringUtils.uncapitalize(aClass.getSimpleName());
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ResourceServerConfig.class);
|
||||
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.ruoyi.common.security.domain;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* 登录用户身份权限
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class LoginUser extends User
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
public LoginUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired,
|
||||
boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities)
|
||||
{
|
||||
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.ruoyi.common.security.feign;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import feign.RequestInterceptor;
|
||||
|
||||
/**
|
||||
* Feign配置注册
|
||||
*
|
||||
* @author ruoyi
|
||||
**/
|
||||
@Configuration
|
||||
public class OAuth2FeignConfig
|
||||
{
|
||||
@Bean
|
||||
public RequestInterceptor requestInterceptor()
|
||||
{
|
||||
return new OAuth2FeignRequestInterceptor();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.ruoyi.common.security.feign;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
|
||||
/**
|
||||
* feign 请求拦截器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class OAuth2FeignRequestInterceptor implements RequestInterceptor
|
||||
{
|
||||
@Override
|
||||
public void apply(RequestTemplate requestTemplate)
|
||||
{
|
||||
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||
Authentication authentication = securityContext.getAuthentication();
|
||||
if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails)
|
||||
{
|
||||
OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails();
|
||||
requestTemplate.header(HttpHeaders.AUTHORIZATION,
|
||||
String.format("%s %s", SecurityConstants.BEARER_TOKEN_TYPE, dateils.getTokenValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.ruoyi.common.security.handler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.ruoyi.common.core.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* 自定义访问无权限资源时的异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class CustomAccessDeniedHandler extends OAuth2AccessDeniedHandler
|
||||
{
|
||||
private final Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
|
||||
{
|
||||
logger.info("权限不足,请联系管理员 {}", request.getRequestURI());
|
||||
|
||||
String msg = authException.getMessage();
|
||||
ServletUtils.renderString(response, JSON.toJSONString(R.fail(HttpStatus.FORBIDDEN, msg)));
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,14 @@ package com.ruoyi.common.security.handler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AccountExpiredException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import com.ruoyi.common.core.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.exception.BaseException;
|
||||
import com.ruoyi.common.core.exception.CustomException;
|
||||
import com.ruoyi.common.core.exception.DemoModeException;
|
||||
import com.ruoyi.common.core.exception.PreAuthorizeException;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||
|
||||
@@ -49,34 +45,6 @@ public class GlobalExceptionHandler
|
||||
return AjaxResult.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoHandlerFoundException.class)
|
||||
public AjaxResult handlerNoFoundException(Exception e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error(HttpStatus.NOT_FOUND, "路径不存在,请检查路径是否正确");
|
||||
}
|
||||
|
||||
@ExceptionHandler(AccessDeniedException.class)
|
||||
public AjaxResult handleAuthorizationException(AccessDeniedException e)
|
||||
{
|
||||
log.error(e.getMessage());
|
||||
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
@ExceptionHandler(AccountExpiredException.class)
|
||||
public AjaxResult handleAccountExpiredException(AccountExpiredException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UsernameNotFoundException.class)
|
||||
public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e)
|
||||
{
|
||||
log.error(e.getMessage(), e);
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public AjaxResult handleException(Exception e)
|
||||
{
|
||||
@@ -105,7 +73,16 @@ public class GlobalExceptionHandler
|
||||
String message = e.getBindingResult().getFieldError().getDefaultMessage();
|
||||
return AjaxResult.error(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 权限异常
|
||||
*/
|
||||
@ExceptionHandler(PreAuthorizeException.class)
|
||||
public AjaxResult preAuthorizeException(PreAuthorizeException e)
|
||||
{
|
||||
return AjaxResult.error("没有权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 演示模式异常
|
||||
*/
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
package com.ruoyi.common.security.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.common.security.domain.LoginUser;
|
||||
import com.ruoyi.common.security.utils.SecurityUtils;
|
||||
|
||||
/**
|
||||
* 自定义权限实现
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service("ss")
|
||||
public class PermissionService
|
||||
{
|
||||
/** 所有权限标识 */
|
||||
private static final String ALL_PERMISSION = "*:*:*";
|
||||
|
||||
/** 管理员角色权限标识 */
|
||||
private static final String SUPER_ADMIN = "admin";
|
||||
|
||||
private static final String ROLE_DELIMETER = ",";
|
||||
|
||||
private static final String PERMISSION_DELIMETER = ",";
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public boolean hasPermi(String permission)
|
||||
{
|
||||
if (StringUtils.isEmpty(permission))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return hasPermissions(loginUser.getAuthorities(), permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
|
||||
*
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否不具备某权限
|
||||
*/
|
||||
public boolean lacksPermi(String permission)
|
||||
{
|
||||
return hasPermi(permission) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限
|
||||
*
|
||||
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public boolean hasAnyPermi(String permissions)
|
||||
{
|
||||
if (StringUtils.isEmpty(permissions))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Collection<? extends GrantedAuthority> authorities = loginUser.getAuthorities();
|
||||
for (String permission : permissions.split(PERMISSION_DELIMETER))
|
||||
{
|
||||
if (permission != null && hasPermissions(authorities, permission))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否拥有某个角色
|
||||
*
|
||||
* @param role 角色字符串
|
||||
* @return 用户是否具备某角色
|
||||
*/
|
||||
public boolean hasRole(String role)
|
||||
{
|
||||
if (StringUtils.isEmpty(role))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (GrantedAuthority authorities : loginUser.getAuthorities())
|
||||
{
|
||||
String roleKey = authorities.getAuthority();
|
||||
if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(role))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某角色,与 isRole逻辑相反。
|
||||
*
|
||||
* @param role 角色名称
|
||||
* @return 用户是否不具备某角色
|
||||
*/
|
||||
public boolean lacksRole(String role)
|
||||
{
|
||||
return hasRole(role) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色
|
||||
*
|
||||
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public boolean hasAnyRoles(String roles)
|
||||
{
|
||||
if (StringUtils.isEmpty(roles))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (StringUtils.isEmpty(loginUser) || CollectionUtils.isEmpty(loginUser.getAuthorities()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String role : roles.split(ROLE_DELIMETER))
|
||||
{
|
||||
if (hasRole(role))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否包含权限
|
||||
*
|
||||
* @param authorities 权限列表
|
||||
* @param permission 权限字符串
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
private boolean hasPermissions(Collection<? extends GrantedAuthority> authorities, String permission)
|
||||
{
|
||||
return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
|
||||
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(permission, x));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.ruoyi.common.security.service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
|
||||
import com.ruoyi.common.core.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||
|
||||
/**
|
||||
* 重写原生方法支持redis缓存
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class RedisClientDetailsService extends JdbcClientDetailsService
|
||||
{
|
||||
public RedisClientDetailsService(DataSource dataSource)
|
||||
{
|
||||
super(dataSource);
|
||||
super.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
|
||||
super.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
|
||||
public ClientDetails loadClientByClientId(String clientId)
|
||||
{
|
||||
return super.loadClientByClientId(clientId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ruoyi.common.security.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.core.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.constant.Constants;
|
||||
import com.ruoyi.common.core.utils.IdUtils;
|
||||
import com.ruoyi.common.core.utils.ServletUtils;
|
||||
import com.ruoyi.common.redis.service.RedisService;
|
||||
import com.ruoyi.system.api.model.LoginUser;
|
||||
|
||||
/**
|
||||
* token验证处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class TokenService
|
||||
{
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;
|
||||
|
||||
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
|
||||
|
||||
protected static final long MILLIS_SECOND = 1000;
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
*/
|
||||
public Map<String, Object> createToken(LoginUser loginUser)
|
||||
{
|
||||
// 生成token
|
||||
String token = IdUtils.fastUUID();
|
||||
loginUser.setToken(token);
|
||||
loginUser.setUserid(loginUser.getSysUser().getUserId());
|
||||
loginUser.setUsername(loginUser.getSysUser().getUserName());
|
||||
refreshToken(loginUser);
|
||||
|
||||
// 保存或更新用户token
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("access_token", token);
|
||||
map.put("expires_in", EXPIRE_TIME);
|
||||
redisService.setCacheObject(ACCESS_TOKEN + token, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public LoginUser getLoginUser()
|
||||
{
|
||||
return getLoginUser(ServletUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户身份信息
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
public LoginUser getLoginUser(HttpServletRequest request)
|
||||
{
|
||||
// 获取请求携带的令牌
|
||||
String token = getToken(request);
|
||||
if (StringUtils.isNotEmpty(token))
|
||||
{
|
||||
String userKey = getTokenKey(token);
|
||||
LoginUser user = redisService.getCacheObject(userKey);
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void delLoginUser(String token)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(token))
|
||||
{
|
||||
String userKey = getTokenKey(token);
|
||||
redisService.deleteObject(userKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌有效期
|
||||
*
|
||||
* @param loginUser 登录信息
|
||||
*/
|
||||
public Long refreshToken(LoginUser loginUser)
|
||||
{
|
||||
loginUser.setLoginTime(System.currentTimeMillis());
|
||||
loginUser.setExpireTime(loginUser.getLoginTime() + EXPIRE_TIME * MILLIS_SECOND);
|
||||
// 根据uuid将loginUser缓存
|
||||
String userKey = getTokenKey(loginUser.getToken());
|
||||
redisService.setCacheObject(userKey, loginUser, EXPIRE_TIME, TimeUnit.SECONDS);
|
||||
return EXPIRE_TIME;
|
||||
}
|
||||
|
||||
private String getTokenKey(String token)
|
||||
{
|
||||
return ACCESS_TOKEN + token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求token
|
||||
*/
|
||||
private String getToken(HttpServletRequest request)
|
||||
{
|
||||
String token = request.getHeader(CacheConstants.HEADER);
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(CacheConstants.TOKEN_PREFIX))
|
||||
{
|
||||
token = token.replace(CacheConstants.TOKEN_PREFIX, "");
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.ruoyi.common.security.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
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.Service;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.enums.UserStatus;
|
||||
import com.ruoyi.common.core.exception.BaseException;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.security.domain.LoginUser;
|
||||
import com.ruoyi.system.api.RemoteUserService;
|
||||
import com.ruoyi.system.api.domain.SysUser;
|
||||
import com.ruoyi.system.api.model.UserInfo;
|
||||
|
||||
/**
|
||||
* 用户信息处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Service
|
||||
public class UserDetailsServiceImpl implements UserDetailsService
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
|
||||
|
||||
@Autowired
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username)
|
||||
{
|
||||
R<UserInfo> userResult = remoteUserService.getUserInfo(username);
|
||||
checkUser(userResult, username);
|
||||
return getUserDetails(userResult);
|
||||
}
|
||||
|
||||
public void checkUser(R<UserInfo> userResult, String username)
|
||||
{
|
||||
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
|
||||
{
|
||||
log.info("登录用户:{} 不存在.", username);
|
||||
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
else if (UserStatus.DELETED.getCode().equals(userResult.getData().getSysUser().getDelFlag()))
|
||||
{
|
||||
log.info("登录用户:{} 已被删除.", username);
|
||||
throw new BaseException("对不起,您的账号:" + username + " 已被删除");
|
||||
}
|
||||
else if (UserStatus.DISABLE.getCode().equals(userResult.getData().getSysUser().getStatus()))
|
||||
{
|
||||
log.info("登录用户:{} 已被停用.", username);
|
||||
throw new BaseException("对不起,您的账号:" + username + " 已停用");
|
||||
}
|
||||
}
|
||||
|
||||
private UserDetails getUserDetails(R<UserInfo> result)
|
||||
{
|
||||
UserInfo info = result.getData();
|
||||
Set<String> dbAuthsSet = new HashSet<String>();
|
||||
if (StringUtils.isNotEmpty(info.getRoles()))
|
||||
{
|
||||
// 获取角色
|
||||
dbAuthsSet.addAll(info.getRoles());
|
||||
// 获取权限
|
||||
dbAuthsSet.addAll(info.getPermissions());
|
||||
}
|
||||
|
||||
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
SysUser user = info.getSysUser();
|
||||
|
||||
return new LoginUser(user.getUserId(), user.getUserName(), user.getPassword(), true, true, true, true,
|
||||
authorities);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.ruoyi.common.security.utils;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import com.ruoyi.common.security.domain.LoginUser;
|
||||
import com.ruoyi.common.core.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.core.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* 权限获取工具类
|
||||
@@ -12,46 +12,31 @@ import com.ruoyi.common.security.domain.LoginUser;
|
||||
*/
|
||||
public class SecurityUtils
|
||||
{
|
||||
/**
|
||||
* 获取Authentication
|
||||
*/
|
||||
public static Authentication getAuthentication()
|
||||
{
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
*/
|
||||
public static String getUsername()
|
||||
{
|
||||
return getLoginUser().getUsername();
|
||||
return ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
* 获取用户ID
|
||||
*/
|
||||
public static LoginUser getLoginUser(Authentication authentication)
|
||||
public static Long getUserId()
|
||||
{
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof LoginUser)
|
||||
{
|
||||
return (LoginUser) principal;
|
||||
}
|
||||
return null;
|
||||
return Convert.toLong(ServletUtils.getRequest().getHeader(CacheConstants.DETAILS_USER_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public static LoginUser getLoginUser()
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
Authentication authentication = getAuthentication();
|
||||
if (authentication == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return getLoginUser(authentication);
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,15 +63,4 @@ public class SecurityUtils
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为管理员
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.ruoyi.common.security.service.UserDetailsServiceImpl,\
|
||||
com.ruoyi.common.security.service.PermissionService,\
|
||||
com.ruoyi.common.security.config.MethodSecurityConfig,\
|
||||
com.ruoyi.common.security.handler.CustomAccessDeniedHandler,\
|
||||
com.ruoyi.common.security.service.TokenService,\
|
||||
com.ruoyi.common.security.aspect.PreAuthorizeAspect,\
|
||||
com.ruoyi.common.security.handler.GlobalExceptionHandler
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.ruoyi.common.swagger.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -15,11 +14,9 @@ import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ApiKey;
|
||||
import springfox.documentation.service.AuthorizationScope;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.GrantType;
|
||||
import springfox.documentation.service.OAuth;
|
||||
import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant;
|
||||
import springfox.documentation.service.SecurityReference;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
@@ -66,54 +63,55 @@ public class SwaggerAutoConfiguration
|
||||
List<Predicate<String>> excludePath = new ArrayList<>();
|
||||
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
|
||||
|
||||
//noinspection Guava
|
||||
//noinspection Guava
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.host(swaggerProperties.getHost())
|
||||
.apiInfo(apiInfo(swaggerProperties)).select()
|
||||
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()))
|
||||
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
|
||||
.build()
|
||||
.securitySchemes(Collections.singletonList(securitySchema()))
|
||||
.securityContexts(Collections.singletonList(securityContext()))
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts())
|
||||
.pathMapping("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private SecurityContext securityContext()
|
||||
/**
|
||||
* 安全模式,这里指定token通过Authorization头请求头传递
|
||||
*/
|
||||
private List<ApiKey> securitySchemes()
|
||||
{
|
||||
return SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex(swaggerProperties().getAuthorization().getAuthRegex()))
|
||||
.build();
|
||||
List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
|
||||
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
|
||||
return apiKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的全局鉴权策略
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
/**
|
||||
* 安全上下文
|
||||
*/
|
||||
private List<SecurityContext> securityContexts()
|
||||
{
|
||||
List<SecurityContext> securityContexts = new ArrayList<>();
|
||||
securityContexts.add(
|
||||
SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex("^(?!auth).*$"))
|
||||
.build());
|
||||
return securityContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的全局鉴权策略
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<SecurityReference> defaultAuth()
|
||||
{
|
||||
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
|
||||
return Collections.singletonList(SecurityReference.builder()
|
||||
.reference(swaggerProperties().getAuthorization().getName())
|
||||
.scopes(authorizationScopeList.toArray(authorizationScopes))
|
||||
.build());
|
||||
}
|
||||
|
||||
private OAuth securitySchema()
|
||||
{
|
||||
ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getAuthorizationScopeList().forEach(authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(), authorizationScope.getDescription())));
|
||||
ArrayList<GrantType> grantTypes = new ArrayList<>();
|
||||
swaggerProperties().getAuthorization().getTokenUrlList().forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
|
||||
return new OAuth(swaggerProperties().getAuthorization().getName(), authorizationScopeList, grantTypes);
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
List<SecurityReference> securityReferences = new ArrayList<>();
|
||||
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
|
||||
return securityReferences;
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties swaggerProperties)
|
||||
@@ -128,5 +126,4 @@ public class SwaggerAutoConfiguration
|
||||
.version(swaggerProperties.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user