♻️ 增加 NoToken 注解方便对应 无token feign调用场景

(cherry picked from commit 2d78533a29c9c392f75d76cf202ae1daf8f7c77b)
This commit is contained in:
冷冷 committed 2024-08-16 15:16:20 +08:00
1 parent 835d582b3b
commit aba51bbdb8
27 files changed
+449 -355

No files matched your search

@@ -20,14 +20,12 @@
package com.pig4cloud.pigx.app.api.feign;
import com.pig4cloud.pigx.app.api.dto.AppUserInfo;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestHeader;
/**
* @author lengleng
@@ -42,8 +40,9 @@ public interface RemoteAppUserService {
* @param from 调用标志
* @return R
*/
@NoToken
@GetMapping("/appuser/info/{username}")
R<AppUserInfo> info(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
R<AppUserInfo> info(@PathVariable("username") String username);
/**
* 通过社交账号或手机号查询用户、角色信息
@@ -51,16 +50,9 @@ public interface RemoteAppUserService {
* @param from 调用标志
* @return
*/
@NoToken
@GetMapping("/appsocial/info/{inStr}")
R<AppUserInfo> social(@PathVariable("inStr") String inStr, @RequestHeader(SecurityConstants.FROM) String from);
R<AppUserInfo> social(@PathVariable("inStr") String inStr);
/**
* 锁定用户
* @param username 用户名
* @param from 调用标识
* @return
*/
@PutMapping("/appuser/lock/{username}")
R<Boolean> lockUser(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
}
@@ -103,7 +103,7 @@ public class PigxTokenEndpoint {
modelAndView.setViewName("ftl/login");
modelAndView.addObject("error", error);
R<List<SysTenant>> tenantList = tenantService.list(SecurityConstants.FROM_IN);
R<List<SysTenant>> tenantList = tenantService.list();
modelAndView.addObject("tenantList", tenantList.getData());
return modelAndView;
}
@@ -114,7 +114,7 @@ public class PigxTokenEndpoint {
@RequestParam(OAuth2ParameterNames.SCOPE) String scope,
@RequestParam(OAuth2ParameterNames.STATE) String state) {
SysOauthClientDetails clientDetails = RetOps
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN))
.of(clientDetailsService.getClientDetailsById(clientId))
.getData()
.orElseThrow(() -> new OAuthClientException("clientId 不合法"));
@@ -59,114 +59,114 @@ import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
public class PigxAuthenticationFailureEventHandler implements AuthenticationFailureHandler {
private static final MappingJackson2HttpMessageConverter errorHttpResponseConverter = new MappingJackson2HttpMessageConverter();
private static final MappingJackson2HttpMessageConverter errorHttpResponseConverter = new MappingJackson2HttpMessageConverter();
private final StringRedisTemplate redisTemplate;
private final StringRedisTemplate redisTemplate;
private final ApplicationEventPublisher publisher;
private final ApplicationEventPublisher publisher;
private final KeyStrResolver tenantKeyStrResolver;
private final KeyStrResolver tenantKeyStrResolver;
private final RemoteUserService userService;
private final RemoteUserService userService;
/**
* Called when an authentication attempt fails.
*
* @param request the request during which the authentication attempt occurred.
* @param response the response.
* @param exception the exception which was thrown to reject the authentication
* request.
*/
@Override
@SneakyThrows
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
log.info("登录失败,异常:{}", exception.getLocalizedMessage());
/**
* Called when an authentication attempt fails.
* @param request the request during which the authentication attempt occurred.
* @param response the response.
* @param exception the exception which was thrown to reject the authentication
* request.
*/
@Override
@SneakyThrows
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
log.info("登录失败,异常:{}", exception.getLocalizedMessage());
// 密码模式记录错误信息
if (OAuth2ParameterNames.PASSWORD.equals(grantType)) {
String username = request.getParameter(OAuth2ParameterNames.USERNAME);
// 密码模式记录错误信息
if (OAuth2ParameterNames.PASSWORD.equals(grantType)) {
String username = request.getParameter(OAuth2ParameterNames.USERNAME);
// 密码错误记录错误次数 (TOC用户不记录)
String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC);
if (exception instanceof OAuth2AuthenticationException
&& !SecurityConstants.HEADER_TOC_YES.equals(header)) {
recordLoginFailureTimes(username);
}
// 密码错误记录错误次数 (TOC用户不记录)
String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC);
if (exception instanceof OAuth2AuthenticationException
&& !SecurityConstants.HEADER_TOC_YES.equals(header)) {
recordLoginFailureTimes(username);
}
// 记录登录失败错误信息
sendFailureEventLog(request, exception, username);
}
// 记录登录失败错误信息
sendFailureEventLog(request, exception, username);
}
// 写出错误信息
sendErrorResponse(request, response, exception);
}
// 写出错误信息
sendErrorResponse(request, response, exception);
}
/**
* 记录失败日志
*
* @param request HttpServletRequest
* @param exception 错误日志
* @param username 用户名
*/
private void sendFailureEventLog(HttpServletRequest request, AuthenticationException exception, String username) {
SysLogDTO logVo = SysLogUtils.getSysLog();
logVo.setTitle("登录失败");
logVo.setLogType(LogTypeEnum.ERROR.getType());
logVo.setException(exception.getLocalizedMessage());
// 发送异步日志事件
String startTimeStr = request.getHeader(CommonConstants.REQUEST_START_TIME);
if (StrUtil.isNotBlank(startTimeStr)) {
Long startTime = Long.parseLong(startTimeStr);
Long endTime = System.currentTimeMillis();
logVo.setTime(endTime - startTime);
}
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
String clientId = WebUtils.extractClientId(header).orElse(null);
logVo.setServiceId(clientId);
logVo.setCreateBy(username);
logVo.setTenantId(Long.parseLong(tenantKeyStrResolver.key()));
publisher.publishEvent(new SysLogEvent(logVo));
}
/**
* 记录失败日志
* @param request HttpServletRequest
* @param exception 错误日志
* @param username 用户名
*/
private void sendFailureEventLog(HttpServletRequest request, AuthenticationException exception, String username) {
SysLogDTO logVo = SysLogUtils.getSysLog();
logVo.setTitle("登录失败");
logVo.setLogType(LogTypeEnum.ERROR.getType());
logVo.setException(exception.getLocalizedMessage());
// 发送异步日志事件
String startTimeStr = request.getHeader(CommonConstants.REQUEST_START_TIME);
if (StrUtil.isNotBlank(startTimeStr)) {
Long startTime = Long.parseLong(startTimeStr);
Long endTime = System.currentTimeMillis();
logVo.setTime(endTime - startTime);
}
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
String clientId = WebUtils.extractClientId(header).orElse(null);
logVo.setServiceId(clientId);
logVo.setCreateBy(username);
logVo.setTenantId(Long.parseLong(tenantKeyStrResolver.key()));
publisher.publishEvent(new SysLogEvent(logVo));
}
/**
* 记录登录失败此处,如果操作阈值 调用接口锁定用户
*
* @param username username
*/
private void recordLoginFailureTimes(String username) {
String key = String.format("%s:%s:%s", tenantKeyStrResolver.key(), CacheConstants.LOGIN_ERROR_TIMES, username);
Long deltaTimes = ParamResolver.getLong("LOGIN_ERROR_TIMES", 5L);
Long times = redisTemplate.opsForValue().increment(key);
/**
* 记录登录失败此处,如果操作阈值 调用接口锁定用户
* @param username username
*/
private void recordLoginFailureTimes(String username) {
String key = String.format("%s:%s:%s", tenantKeyStrResolver.key(), CacheConstants.LOGIN_ERROR_TIMES, username);
Long deltaTimes = ParamResolver.getLong("LOGIN_ERROR_TIMES", 5L);
Long times = redisTemplate.opsForValue().increment(key);
// 自动过期时间
Long deltaTime = ParamResolver.getLong("DELTA_TIME", 1L);
redisTemplate.expire(key, deltaTime, TimeUnit.HOURS);
// 自动过期时间
Long deltaTime = ParamResolver.getLong("DELTA_TIME", 1L);
redisTemplate.expire(key, deltaTime, TimeUnit.HOURS);
if (deltaTimes <= times) {
userService.lockUser(username, SecurityConstants.FROM_IN);
}
}
if (deltaTimes <= times) {
userService.lockUser(username);
}
}
private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
httpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
String errorMessage;
private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException {
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
httpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
String errorMessage;
if (exception instanceof OAuth2AuthenticationException) {
OAuth2AuthenticationException authorizationException = (OAuth2AuthenticationException) exception;
errorMessage = StrUtil.isBlank(authorizationException.getError().getDescription())
? authorizationException.getError().getErrorCode()
: authorizationException.getError().getDescription();
if (exception instanceof OAuth2AuthenticationException) {
OAuth2AuthenticationException authorizationException = (OAuth2AuthenticationException) exception;
errorMessage = StrUtil.isBlank(authorizationException.getError().getDescription())
? authorizationException.getError().getErrorCode()
: authorizationException.getError().getDescription();
this.errorHttpResponseConverter.write(R.failed(authorizationException.getError().getErrorCode(), errorMessage), MediaType.APPLICATION_JSON, httpResponse);
this.errorHttpResponseConverter.write(
R.failed(authorizationException.getError().getErrorCode(), errorMessage),
MediaType.APPLICATION_JSON, httpResponse);
} else {
errorMessage = exception.getLocalizedMessage();
this.errorHttpResponseConverter.write(R.failed(errorMessage), MediaType.APPLICATION_JSON, httpResponse);
}
}
}
else {
errorMessage = exception.getLocalizedMessage();
this.errorHttpResponseConverter.write(R.failed(errorMessage), MediaType.APPLICATION_JSON, httpResponse);
}
}
}
@@ -77,7 +77,7 @@ public class DefaultAuditLogHandle implements IAuditLogHandle {
@Async
public void asyncSend(List<SysAuditLog> auditLogList) {
remoteLogService.saveLog(auditLogList, SecurityConstants.FROM_IN);
remoteLogService.saveLog(auditLogList);
}
}
@@ -3,7 +3,6 @@ package com.pig4cloud.pigx.common.data.resolver;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.admin.api.feign.RemoteParamService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import lombok.experimental.UtilityClass;
@@ -31,7 +30,7 @@ public class ParamResolver {
}
RemoteParamService remoteParamService = SpringContextHolder.getBean(RemoteParamService.class);
return remoteParamService.getByKeys(key, SecurityConstants.FROM_IN).getData();
return remoteParamService.getByKeys(key).getData();
}
/**
@@ -62,7 +61,7 @@ public class ParamResolver {
RemoteParamService remoteParamService = SpringContextHolder.getBean(RemoteParamService.class);
String result = remoteParamService.getByKey(key, SecurityConstants.FROM_IN).getData();
String result = remoteParamService.getByKey(key).getData();
if (StrUtil.isNotBlank(result)) {
return Convert.convert(clazz, result);
+5
View File
@@ -28,5 +28,10 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 基础依赖 -->
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-core</artifactId>
</dependency>
</dependencies>
</project>
@@ -18,6 +18,8 @@
package com.pig4cloud.pigx.common.feign;
import com.pig4cloud.pigx.common.feign.endpoint.FeignClientEndpoint;
import com.pig4cloud.pigx.common.feign.interceptor.PigxFeignInnerRequestInterceptor;
import com.pig4cloud.pigx.common.feign.interceptor.PigxFeignRequestCloseInterceptor;
import feign.Feign;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -42,11 +44,35 @@ import org.springframework.context.annotation.Import;
@AutoConfigureAfter(EnableFeignClients.class)
public class PigxFeignAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public FeignClientEndpoint feignClientEndpoint(ApplicationContext context) {
return new FeignClientEndpoint(context);
}
/**
* feign actuator endpoint
* @param context
* @return FeignClientEndpoint
*/
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public FeignClientEndpoint feignClientEndpoint(ApplicationContext context) {
return new FeignClientEndpoint(context);
}
/**
* add http connection close header
*
* @return PigxFeignRequestCloseInterceptor
*/
@Bean
public PigxFeignRequestCloseInterceptor pigFeignRequestCloseInterceptor() {
return new PigxFeignRequestCloseInterceptor();
}
/**
* add inner request header
*
* @return PigxFeignInnerRequestInterceptor
*/
@Bean
public PigxFeignInnerRequestInterceptor pigFeignInnerRequestInterceptor() {
return new PigxFeignInnerRequestInterceptor();
}
}
@@ -0,0 +1,16 @@
package com.pig4cloud.pigx.common.feign.annotation;
import java.lang.annotation.*;
/**
* 服务无token调用声明注解
* <p>
* 只有发起方没有 token 时候才需要添加此注解, @NoToken + @Inner
* <p>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoToken {
}
@@ -0,0 +1,36 @@
package com.pig4cloud.pigx.common.feign.interceptor;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.core.Ordered;
import java.lang.reflect.Method;
/**
* @author lengleng
* @date 2024/6/1
*/
public class PigxFeignInnerRequestInterceptor implements RequestInterceptor, Ordered {
/**
* Called for every request. Add data using methods on the supplied
* {@link RequestTemplate}.
* @param template
*/
@Override
public void apply(RequestTemplate template) {
Method method = template.methodMetadata().method();
NoToken noToken = method.getAnnotation(NoToken.class);
if (noToken != null) {
template.header(SecurityConstants.FROM, SecurityConstants.FROM_IN);
}
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
}
@@ -0,0 +1,23 @@
package com.pig4cloud.pigx.common.feign.interceptor;
import feign.RequestInterceptor;
import org.springframework.http.HttpHeaders;
/**
* @author lengleng
* @date 2024/3/15
* <p>
* http connection close
*/
public class PigxFeignRequestCloseInterceptor implements RequestInterceptor {
/**
* set connection close
* @param template
*/
@Override
public void apply(feign.RequestTemplate template) {
template.header(HttpHeaders.CONNECTION, "close");
}
}
@@ -18,11 +18,16 @@ package com.pig4cloud.pigx.common.log.event;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.admin.api.feign.RemoteLogService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.jackson.PigxJavaTimeModule;
import com.pig4cloud.pigx.common.log.config.PigxLogProperties;
import com.pig4cloud.pigx.common.log.util.JacksonSensitiveFieldUtil;
import jakarta.servlet.ServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@@ -44,50 +49,62 @@ import java.util.Objects;
@RequiredArgsConstructor
public class SysLogListener implements InitializingBean {
/**
* 忽略序列化的对象类型
*/
private final static Class[] ignoreClass = {ServletRequest.class, BindingResult.class};
/**
* 忽略序列化的对象类型
*/
private final static Class[] ignoreClass = { ServletRequest.class, BindingResult.class };
/**
* new 一个 避免日志脱敏策略影响全局ObjectMapper
*/
/**
* new 一个 避免日志脱敏策略影响全局ObjectMapper
*/
private final static ObjectMapper objectMapper = new ObjectMapper();
private final RemoteLogService remoteLogService;
private final RemoteLogService remoteLogService;
private final PigxLogProperties logProperties;
private final PigxLogProperties logProperties;
@SneakyThrows
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
SysLogDTO source = (SysLogDTO) event.getSource();
@SneakyThrows
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
SysLogDTO source = (SysLogDTO) event.getSource();
// json 格式刷参数放在异步中处理,提升性能
if (Objects.nonNull(source.getBody()) && logProperties.isRequestEnabled()) {
Object[] args = (Object[]) source.getBody();
List<Object> list = CollUtil.toList(args);
// 删除部分无法序列化的参数
list.removeIf(obj -> Arrays.stream(ignoreClass).anyMatch(clazz -> clazz.isAssignableFrom(obj.getClass())));
// json 格式刷参数放在异步中处理,提升性能
if (Objects.nonNull(source.getBody()) && logProperties.isRequestEnabled()) {
Object[] args = (Object[]) source.getBody();
List<Object> list = CollUtil.toList(args);
// 删除部分无法序列化的参数
list.removeIf(obj -> Arrays.stream(ignoreClass).anyMatch(clazz -> clazz.isAssignableFrom(obj.getClass())));
try {
// 序列化参数
String params = JacksonSensitiveFieldUtil.getObjectMapper().writeValueAsString(list);
source.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
} catch (Exception e) {
log.error("请求参数序列化异常:{}", e.getMessage());
}
}
try {
// 序列化参数
String params = objectMapper.writeValueAsString(list);
source.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
}
catch (Exception e) {
log.error("请求参数序列化异常:{}", e.getMessage());
}
}
source.setBody(null);
remoteLogService.saveLog(source);
}
source.setBody(null);
remoteLogService.saveLog(source);
}
@Override
public void afterPropertiesSet() {
objectMapper.addMixIn(Object.class, PropertyFilterMixIn.class);
String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",
SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
objectMapper.setFilterProvider(filters);
objectMapper.registerModule(new PigxJavaTimeModule());
}
@JsonFilter("filter properties by name")
class PropertyFilterMixIn {
}
@Override
public void afterPropertiesSet() {
String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
JacksonSensitiveFieldUtil.configureSensitiveFields(ignorableFieldNames);
JacksonSensitiveFieldUtil.registerCustomModule(new PigxJavaTimeModule());
}
}
@@ -20,7 +20,6 @@ package com.pig4cloud.pigx.common.security.service;
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.enums.UserTypeEnum;
import com.pig4cloud.pigx.common.core.util.R;
import lombok.RequiredArgsConstructor;
@@ -60,7 +59,7 @@ public class PigxDefaultUserDetailsServiceImpl implements PigxUserDetailsService
return cache.get(username, PigxUser.class);
}
R<UserInfo> result = remoteUserService.info(username, SecurityConstants.FROM_IN);
R<UserInfo> result = remoteUserService.info(username);
UserDetails userDetails = getUserDetails(result);
cache.put(username, userDetails);
return userDetails;
@@ -24,7 +24,7 @@ public class PigxMobileUserDetailServiceImpl implements PigxUserDetailsService {
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String phone) {
R<UserInfo> result = remoteUserService.social(phone, SecurityConstants.FROM_IN);
R<UserInfo> result = remoteUserService.social(phone);
return getUserDetails(result);
}
@@ -88,7 +88,7 @@ public class PigxRemoteRegisteredClientRepository implements RegisteredClientRep
public RegisteredClient findByClientId(String clientId) {
SysOauthClientDetails clientDetails = RetOps
.of(clientDetailsService.getClientDetailsById(clientId, SecurityConstants.FROM_IN))
.of(clientDetailsService.getClientDetailsById(clientId))
.getData()
.orElseThrow(() -> new OAuth2AuthorizationCodeRequestAuthenticationException(
new OAuth2Error("客户端查询异常,请检查数据库链接"), null));
@@ -51,93 +51,93 @@ import java.util.*;
@RequiredArgsConstructor
public class PigxTocDefaultUserDetailsServiceImpl implements PigxUserDetailsService {
private final CacheManager cacheManager;
private final CacheManager cacheManager;
private final Optional<RemoteAppUserService> remoteAppUserServiceOptional;
private final Optional<RemoteAppUserService> remoteAppUserServiceOptional;
/**
* 用户密码登录
*
* @param username 用户密码登录
* @return
*/
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
if (!remoteAppUserServiceOptional.isPresent()) {
throw new UnsupportedOperationException();
}
/**
* 用户密码登录
* @param username 用户密码登录
* @return
*/
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String username) {
if (!remoteAppUserServiceOptional.isPresent()) {
throw new UnsupportedOperationException();
}
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI);
if (cache != null && cache.get(username) != null) {
return cache.get(username, PigxUser.class);
}
Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS_MINI);
if (cache != null && cache.get(username) != null) {
return cache.get(username, PigxUser.class);
}
R<AppUserInfo> info = remoteAppUserServiceOptional.get().info(username, SecurityConstants.FROM_IN);
UserDetails userDetailsAppUser = this.getUserDetailsAppUser(info);
if (cache != null) {
cache.put(username, userDetailsAppUser);
}
return userDetailsAppUser;
}
R<AppUserInfo> info = remoteAppUserServiceOptional.get().info(username);
UserDetails userDetailsAppUser = this.getUserDetailsAppUser(info);
if (cache != null) {
cache.put(username, userDetailsAppUser);
}
return userDetailsAppUser;
}
@Override
public UserDetails loadUserByUser(PigxUser pigxUser) {
return pigxUser;
}
@Override
public UserDetails loadUserByUser(PigxUser pigxUser) {
return pigxUser;
}
UserDetails getUserDetailsAppUser(R<AppUserInfo> result) {
// @formatter:off
UserDetails getUserDetailsAppUser(R<AppUserInfo> result) {
// @formatter:off
return RetOps.of(result)
.assertSuccess(r -> new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "用户信息获取失败",null)))
.getData()
.map(this::convertUserDetailsAppUser)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
// @formatter:on
}
}
/**
* UserInfo 转 UserDetails
*
* @param info
* @return 返回UserDetails对象
*/
UserDetails convertUserDetailsAppUser(AppUserInfo info) {
Set<String> dbAuthsSet = new HashSet<>();
if (ArrayUtil.isNotEmpty(info.getRoles())) {
// 获取角色
Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId));
// 获取资源
dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
/**
* UserInfo 转 UserDetails
* @param info
* @return 返回UserDetails对象
*/
UserDetails convertUserDetailsAppUser(AppUserInfo info) {
Set<String> dbAuthsSet = new HashSet<>();
if (ArrayUtil.isNotEmpty(info.getRoles())) {
// 获取角色
Arrays.stream(info.getRoles()).forEach(roleId -> dbAuthsSet.add(SecurityConstants.ROLE + roleId));
// 获取资源
dbAuthsSet.addAll(Arrays.asList(info.getPermissions()));
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
AppUser user = info.getAppUser();
// 构造security用户
}
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
AppUser user = info.getAppUser();
// 构造security用户
return new PigxUser(user.getUserId(), user.getUsername(), null, user.getPhone(), user.getAvatar(),
user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(),
SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true,
!CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities);
}
return new PigxUser(user.getUserId(), user.getUsername(), null, user.getPhone(), user.getAvatar(),
user.getNickname(), user.getName(), user.getEmail(), user.getTenantId(),
SecurityConstants.BCRYPT + user.getPassword(), true, true, UserTypeEnum.TOC.getStatus(), true,
!CommonConstants.STATUS_LOCK.equals(user.getLockFlag()), authorities);
}
@Override
public int getOrder() {
return 10;
}
@Override
public int getOrder() {
return 10;
}
/**
* 支持所有的 mobile 类型
*
* @param clientId 目标客户端
* @param grantType 授权类型
* @return true/false
*/
@Override
public boolean support(String clientId, String grantType) {
String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC);
return SecurityConstants.HEADER_TOC_YES.equals(header);
}
/**
* 支持所有的 mobile 类型
* @param clientId 目标客户端
* @param grantType 授权类型
* @return true/false
*/
@Override
public boolean support(String clientId, String grantType) {
if (Objects.isNull(WebUtils.getRequest())) {
return false;
}
String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC);
return SecurityConstants.HEADER_TOC_YES.equals(header);
}
}
@@ -62,7 +62,7 @@ public class PigxTocMobileUserDetailsServiceImpl implements PigxUserDetailsServi
if (!remoteAppUserServiceOptional.isPresent()) {
throw new UnsupportedOperationException();
}
R<AppUserInfo> info = remoteAppUserServiceOptional.get().social(phone, SecurityConstants.FROM_IN);
R<AppUserInfo> info = remoteAppUserServiceOptional.get().social(phone);
return this.getUserDetailsAppUser(info);
}
@@ -121,6 +121,10 @@ public class PigxTocMobileUserDetailsServiceImpl implements PigxUserDetailsServi
*/
@Override
public boolean support(String clientId, String grantType) {
if (Objects.isNull(WebUtils.getRequest())) {
return false;
}
String header = WebUtils.getRequest().getHeader(SecurityConstants.HEADER_TOC);
return SecurityConstants.HEADER_TOC_YES.equals(header) && SecurityConstants.GRANT_MOBILE.equals(grantType);
}
@@ -20,13 +20,12 @@
package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.admin.api.entity.SysAuditLog;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
@@ -40,10 +39,10 @@ public interface RemoteAuditLogService {
/**
* 保存日志
* @param auditLogList 日志实体 列表
* @param from 是否内部调用
* @return succes、false
*/
@NoToken
@PostMapping("/audit")
R<Boolean> saveLog(@RequestBody List<SysAuditLog> auditLogList, @RequestHeader(SecurityConstants.FROM) String from);
R<Boolean> saveLog(@RequestBody List<SysAuditLog> auditLogList);
}
@@ -20,13 +20,12 @@
package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.admin.api.entity.SysOauthClientDetails;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
@@ -40,19 +39,18 @@ public interface RemoteClientDetailsService {
/**
* 通过clientId 查询客户端信息
* @param clientId 用户名
* @param from 调用标志
* @return R
*/
@NoToken
@GetMapping("/client/getClientDetailsById/{clientId}")
R<SysOauthClientDetails> getClientDetailsById(@PathVariable("clientId") String clientId,
@RequestHeader(SecurityConstants.FROM) String from);
R<SysOauthClientDetails> getClientDetailsById(@PathVariable("clientId") String clientId);
/**
* 查询全部客户端
* @param from 调用标识
* @return R
*/
@NoToken
@GetMapping("/client/list")
R<List<SysOauthClientDetails>> listClientDetails(@RequestHeader(SecurityConstants.FROM) String from);
R<List<SysOauthClientDetails>> listClientDetails();
}
@@ -20,13 +20,12 @@
package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.admin.api.dto.SysLogDTO;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
/**
* @author lengleng
@@ -38,10 +37,10 @@ public interface RemoteLogService {
/**
* 保存日志
* @param sysLog 日志实体
* @param from 是否内部调用
* @return succes、false
*/
@NoToken
@PostMapping("/log/save")
R<Boolean> saveLog(@RequestBody SysLogDTO sysLog, @RequestHeader(SecurityConstants.FROM) String from);
R<Boolean> saveLog(@RequestBody SysLogDTO sysLog);
}
@@ -1,13 +1,11 @@
package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.RetOps;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Map;
@@ -24,20 +22,18 @@ public interface RemoteParamService {
/**
* 通过key 查询参数配置
* @param key key
* @param from 声明成内部调用,避免MQ 等无法调用
* @return
*/
@NoToken
@GetMapping("/param/publicValue/{key}")
R<String> getByKey(@PathVariable("key") String key, @RequestHeader(SecurityConstants.FROM) String from);
R<String> getByKey(@PathVariable("key") String key);
/**
* 通过keys 查询参数配置
* @param keys keys
* @param from 声明成内部调用,避免MQ 等无法调用
* @return map
*/
@GetMapping("/param/publicValues")
R<Map<String, Object>> getByKeys(@RequestParam("keys") String[] keys,
@RequestHeader(SecurityConstants.FROM) String from);
R<Map<String, Object>> getByKeys(@RequestParam("keys") String[] keys);
}
@@ -20,12 +20,11 @@
package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.admin.api.entity.SysTenant;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
@@ -40,10 +39,10 @@ public interface RemoteTenantService {
/**
* 查询全部有效租户
* @param from 内部标志
* @return
*/
@NoToken
@GetMapping("/tenant/list")
R<List<SysTenant>> list(@RequestHeader(SecurityConstants.FROM) String from);
R<List<SysTenant>> list();
}
@@ -19,9 +19,9 @@ package com.pig4cloud.pigx.admin.api.feign;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@@ -36,33 +36,30 @@ public interface RemoteTokenService {
/**
* 分页查询token 信息
* @param from 内部调用标志
* @param params 分页参数
* @param from 内部调用标志
* @return page
*/
@NoToken
@PostMapping("/token/page")
R<Page> getTokenPage(@RequestBody Map<String, Object> params, @RequestHeader(SecurityConstants.FROM) String from);
R<Page> getTokenPage(@RequestBody Map<String, Object> params);
/**
* 删除token
* @param from 内部调用标志
* @param token token
* @param from 内部调用标志
* @return
*/
@NoToken
@DeleteMapping("/token/{token}")
R<Boolean> removeTokenById(@PathVariable("token") String token, @RequestHeader(SecurityConstants.FROM) String from);
R<Boolean> removeTokenById(@PathVariable("token") String token);
/**
* 校验令牌获取用户信息
* @param token
* @param from
* @return
*/
@NoToken
@GetMapping("/token/query-token")
R<Map<String, Object>> queryToken(@RequestParam("token") String token,
@RequestParam(CommonConstants.TENANT_ID) String tenantId,
@RequestHeader(SecurityConstants.FROM) String from);
@RequestParam(CommonConstants.TENANT_ID) String tenantId);
}
@@ -22,9 +22,9 @@ package com.pig4cloud.pigx.admin.api.feign;
import com.pig4cloud.pigx.admin.api.dto.UserDTO;
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
import com.pig4cloud.pigx.admin.api.entity.SysUser;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.NoToken;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
@@ -37,88 +37,80 @@ import java.util.List;
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.UPMS_SERVICE)
public interface RemoteUserService {
/**
* 通过用户名查询用户、角色信息
*
* @param username 用户名
* @param from 调用标志
* @return R
*/
@GetMapping("/user/info/{username}")
R<UserInfo> info(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 通过用户名查询用户、角色信息
* @param username 用户名
* @return R
*/
@NoToken
@GetMapping("/user/info/{username}")
R<UserInfo> info(@PathVariable("username") String username);
/**
* 根据用户ID获取用户
*
* @param userId ID
* @return SysUser
*/
@GetMapping("/user/details/{userId}")
R<SysUser> getUserById(@PathVariable("userId") Long userId);
/**
* 根据用户ID获取用户
* @param userId ID
* @return SysUser
*/
@GetMapping("/user/details/{userId}")
R<SysUser> getUserById(@PathVariable("userId") Long userId);
/**
* 通过社交账号或手机号查询用户、角色信息
*
* @param inStr appid@code
* @param from 调用标志
* @return
*/
@GetMapping("/social/info/{inStr}")
R<UserInfo> social(@PathVariable("inStr") String inStr, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 通过社交账号或手机号查询用户、角色信息
* @param inStr appid@code
* @return
*/
@NoToken
@GetMapping("/social/info/{inStr}")
R<UserInfo> social(@PathVariable("inStr") String inStr);
/**
* 查询上级部门的用户信息
*
* @param username 用户名
* @return R
*/
@GetMapping("/user/ancestor/{username}")
R<List<SysUser>> ancestorUsers(@PathVariable("username") String username);
/**
* 查询上级部门的用户信息
* @param username 用户名
* @return R
*/
@GetMapping("/user/ancestor/{username}")
R<List<SysUser>> ancestorUsers(@PathVariable("username") String username);
/**
* 锁定用户
*
* @param username 用户名
* @param from 调用标识
* @return
*/
@PutMapping("/user/lock/{username}")
R<Boolean> lockUser(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 锁定用户
* @param username 用户名
* @return
*/
@NoToken
@PutMapping("/user/lock/{username}")
R<Boolean> lockUser(@PathVariable("username") String username);
/**
* 根据角色ID查询用户列表
*
* @param roleIdList 角色ID列表
* @return 用户ID列表
*/
@GetMapping("/user/getUserIdListByRoleIdList")
R<List<Long>> getUserIdListByRoleIdList(@RequestParam("roleIdList") List<Long> roleIdList);
/**
* 根据角色ID查询用户列表
* @param roleIdList 角色ID列表
* @return 用户ID列表
*/
@GetMapping("/user/getUserIdListByRoleIdList")
R<List<Long>> getUserIdListByRoleIdList(@RequestParam("roleIdList") List<Long> roleIdList);
/**
* 根据部门ID列表获取用户ID列表接口
*
* @param deptIdList 部门ID列表
* @return 用户ID列表
*/
@GetMapping("/user/getUserIdListByDeptIdList")
R<List<SysUser>> getUserIdListByDeptIdList(@RequestParam("deptIdList") List<Long> deptIdList);
/**
* 根据部门ID列表获取用户ID列表接口
* @param deptIdList 部门ID列表
* @return 用户ID列表
*/
@GetMapping("/user/getUserIdListByDeptIdList")
R<List<SysUser>> getUserIdListByDeptIdList(@RequestParam("deptIdList") List<Long> deptIdList);
/**
* 通过用户名查询用户列表
*
* @param userName 用户名
* @return 用户列表
*/
@GetMapping("/user/getUserListByUserName")
R<List<SysUser>> getUserListByUserName(@RequestParam("username") String userName);
/**
* 通过用户名查询用户列表
* @param userName 用户名
* @return 用户列表
*/
@GetMapping("/user/getUserListByUserName")
R<List<SysUser>> getUserListByUserName(@RequestParam("username") String userName);
/**
* 注册用户
*
* @param userDTO 用户信息
* @return success/false
*/
@PostMapping("/register/user")
R<Boolean> registerUser(@RequestBody UserDTO userDTO, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 注册用户
* @param userDTO 用户信息
* @return success/false
*/
@NoToken
@PostMapping("/register/user")
R<Boolean> registerUser(@RequestBody UserDTO userDTO);
}
@@ -18,7 +18,6 @@
package com.pig4cloud.pigx.admin.controller;
import com.pig4cloud.pigx.admin.api.feign.RemoteTokenService;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@@ -53,7 +52,7 @@ public class SysTokenController {
*/
@RequestMapping("/page")
public R getTokenPage(@RequestBody Map<String, Object> params) {
return remoteTokenService.getTokenPage(params, SecurityConstants.FROM_IN);
return remoteTokenService.getTokenPage(params);
}
/**
@@ -66,7 +65,7 @@ public class SysTokenController {
@PreAuthorize("@pms.hasPermission('sys_token_del')")
public R removeById(@RequestBody String[] tokens) {
for (String token : tokens) {
remoteTokenService.removeTokenById(token, SecurityConstants.FROM_IN);
remoteTokenService.removeTokenById(token);
}
return R.ok();
}
@@ -4,7 +4,6 @@ import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.admin.api.feign.RemoteTokenService;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.core.util.RetOps;
import lombok.RequiredArgsConstructor;
@@ -32,7 +31,7 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
String tenant = StrUtil.subBefore(token, CharUtil.UNDERLINE, false);
String tokenStr = StrUtil.subAfter(token, CharUtil.UNDERLINE, false);
// @formatter:off
return RetOps.of(tokenService.queryToken(tokenStr, tenant ,SecurityConstants.FROM_IN))
return RetOps.of(tokenService.queryToken(tokenStr, tenant ))
.getDataIf(RetOps.CODE_SUCCESS)
.map(o -> (String)o.get("principalName"))
.orElse(null);
@@ -57,7 +56,7 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
// 分割出 PIGX 的租户信息
String tenant = StrUtil.subBefore(token, CharUtil.UNDERLINE, false);
String tokenStr = StrUtil.subAfter(token, CharUtil.UNDERLINE, false);
R<Map<String, Object>> result = tokenService.queryToken(tokenStr,tenant , SecurityConstants.FROM_IN);
R<Map<String, Object>> result = tokenService.queryToken(tokenStr,tenant );
if (CommonConstants.SUCCESS.equals(result.getCode())) {
return true;
}
@@ -3,7 +3,6 @@ package com.pig4cloud.pigx.mp.config;
import com.google.common.collect.Maps;
import com.pig4cloud.pigx.admin.api.feign.RemoteTenantService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.RetOps;
import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.mp.entity.WxAccount;
@@ -97,7 +96,7 @@ public class WxMpInitConfigRunner {
// 获取全部租户 遍历所有租户对应的公众号列表
List<WxAccount> accountList = new ArrayList<>();
// @formatter:off
RetOps.of(tenantService.list(SecurityConstants.FROM_IN))
RetOps.of(tenantService.list())
.getData()
.orElseGet(Collections::emptyList)
.forEach(
@@ -123,7 +122,7 @@ public class WxMpInitConfigRunner {
private WxMpMessageRouter newRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// 记录所有事件的日志 (异步执行)
// 记录所有事件的日志 (异步执行) #5637 大数据量瓶颈
newRouter.rule().handler(this.logHandler).next();
// 接收客服会话管理事件
@@ -79,7 +79,7 @@ public class SecurityRequestFilter extends OncePerRequestFilter {
// 获取用户信息
Optional<String> principalName = RetOps
.of(remoteTokenService.queryToken(accessToken, tenantId, SecurityConstants.FROM_IN))
.of(remoteTokenService.queryToken(accessToken, tenantId))
.getDataIf(RetOps.CODE_SUCCESS)
.map(o -> (String) o.get("principalName"));