mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-22 12:48:59 +08:00
refactor: 框架升级错误问题修复(临时提交勿clone)
This commit is contained in:
parent
14b8bd8d00
commit
184d979b00
@ -69,7 +69,7 @@ public class CartServiceImpl implements CartService {
|
||||
throw new BizException(ResultCode.INVALID_TOKEN);
|
||||
}
|
||||
BoundHashOperations cartHashOperations = getCartHashOperations(memberId);
|
||||
String hKey = skuId + "";
|
||||
String hKey = String.valueOf(skuId);
|
||||
|
||||
CartItemDTO cartItem;
|
||||
// 购物车已存在该商品,更新商品数量
|
||||
|
@ -56,7 +56,6 @@ public class PmsBrandController {
|
||||
}
|
||||
|
||||
@Operation(summary= "品牌详情")
|
||||
@ApiImplicitParam(name = "id", value = "品牌id", required = true, paramType = "path", dataType = "Long")
|
||||
@GetMapping("/{id}")
|
||||
public Result getBrandList(@PathVariable Integer id) {
|
||||
PmsBrand brand = brandService.getById(id);
|
||||
@ -71,10 +70,6 @@ public class PmsBrandController {
|
||||
}
|
||||
|
||||
@Operation(summary= "修改品牌")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "id", value = "品牌id", required = true, paramType = "path", dataType = "Long"),
|
||||
@ApiImplicitParam(name = "brand", value = "实体JSON对象", required = true, paramType = "body", dataType = "PmsBrand")
|
||||
})
|
||||
@PutMapping(value = "/{id}")
|
||||
public Result updateBrand(
|
||||
@PathVariable Long id,
|
||||
|
@ -8,7 +8,7 @@ import com.youlai.common.result.PageResult;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.mall.ums.dto.MemberInfoDTO;
|
||||
import com.youlai.mall.ums.model.entity.UmsMember;
|
||||
import com.youlai.mall.ums.service.IUmsMemberService;
|
||||
import com.youlai.mall.ums.service.UmsMemberService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -24,7 +24,7 @@ import java.util.Arrays;
|
||||
@RequiredArgsConstructor
|
||||
public class UmsMemberController {
|
||||
|
||||
private final IUmsMemberService memberService;
|
||||
private final UmsMemberService memberService;
|
||||
|
||||
@Operation(summary= "会员分页列表")
|
||||
@GetMapping
|
||||
|
@ -4,7 +4,7 @@ import com.youlai.common.result.Result;
|
||||
import com.youlai.mall.ums.dto.MemberAddressDTO;
|
||||
import com.youlai.mall.ums.model.entity.UmsAddress;
|
||||
import com.youlai.mall.ums.model.form.AddressForm;
|
||||
import com.youlai.mall.ums.service.IUmsAddressService;
|
||||
import com.youlai.mall.ums.service.UmsAddressService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -21,7 +21,7 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public class AddressController {
|
||||
|
||||
private final IUmsAddressService addressService;
|
||||
private final UmsAddressService addressService;
|
||||
|
||||
@Operation(summary= "获取当前会员地址列表")
|
||||
@GetMapping
|
||||
|
@ -11,7 +11,7 @@ import com.youlai.mall.ums.dto.MemberAuthDTO;
|
||||
import com.youlai.mall.ums.dto.MemberRegisterDto;
|
||||
import com.youlai.mall.ums.model.entity.UmsMember;
|
||||
import com.youlai.mall.ums.model.vo.MemberVO;
|
||||
import com.youlai.mall.ums.service.IUmsMemberService;
|
||||
import com.youlai.mall.ums.service.UmsMemberService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@ -26,7 +26,7 @@ import java.util.Set;
|
||||
@RequestMapping("/app-api/v1/members")
|
||||
@RequiredArgsConstructor
|
||||
public class MemberController {
|
||||
private final IUmsMemberService memberService;
|
||||
private final UmsMemberService memberService;
|
||||
|
||||
@Operation(summary= "根据会员ID获取openid")
|
||||
@GetMapping("/{memberId}/openid")
|
||||
|
@ -14,7 +14,7 @@ import java.util.List;
|
||||
* @author haoxr
|
||||
* @since 2022/2/12
|
||||
*/
|
||||
public interface IUmsAddressService extends IService<UmsAddress> {
|
||||
public interface UmsAddressService extends IService<UmsAddress> {
|
||||
|
||||
/**
|
||||
* 新增地址
|
@ -21,7 +21,7 @@ import java.util.Set;
|
||||
* @author haoxr
|
||||
* @since 2022/2/12
|
||||
*/
|
||||
public interface IUmsMemberService extends IService<UmsMember> {
|
||||
public interface UmsMemberService extends IService<UmsMember> {
|
||||
|
||||
IPage<UmsMember> list(Page<UmsMember> page, String nickname);
|
||||
|
@ -10,7 +10,7 @@ import com.youlai.mall.ums.dto.MemberAddressDTO;
|
||||
import com.youlai.mall.ums.mapper.UmsAddressMapper;
|
||||
import com.youlai.mall.ums.model.entity.UmsAddress;
|
||||
import com.youlai.mall.ums.model.form.AddressForm;
|
||||
import com.youlai.mall.ums.service.IUmsAddressService;
|
||||
import com.youlai.mall.ums.service.UmsAddressService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -26,7 +26,7 @@ import java.util.stream.Collectors;
|
||||
* @since 2022/2/12
|
||||
*/
|
||||
@Service
|
||||
public class UmsAddressServiceImpl extends ServiceImpl<UmsAddressMapper, UmsAddress> implements IUmsAddressService {
|
||||
public class UmsAddressServiceImpl extends ServiceImpl<UmsAddressMapper, UmsAddress> implements UmsAddressService {
|
||||
|
||||
/**
|
||||
* 新增地址
|
||||
|
@ -20,8 +20,8 @@ import com.youlai.mall.ums.mapper.UmsMemberMapper;
|
||||
import com.youlai.mall.ums.model.entity.UmsAddress;
|
||||
import com.youlai.mall.ums.model.entity.UmsMember;
|
||||
import com.youlai.mall.ums.model.vo.MemberVO;
|
||||
import com.youlai.mall.ums.service.IUmsAddressService;
|
||||
import com.youlai.mall.ums.service.IUmsMemberService;
|
||||
import com.youlai.mall.ums.service.UmsAddressService;
|
||||
import com.youlai.mall.ums.service.UmsMemberService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@ -40,13 +40,13 @@ import java.util.Set;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class UmsMemberServiceImpl extends ServiceImpl<UmsMemberMapper, UmsMember> implements IUmsMemberService {
|
||||
public class UmsMemberServiceImpl extends ServiceImpl<UmsMemberMapper, UmsMember> implements UmsMemberService {
|
||||
|
||||
private final RedisTemplate redisTemplate;
|
||||
private final MemberConvert memberConvert;
|
||||
|
||||
private final AddressConvert addressConvert;
|
||||
private final IUmsAddressService addressService;
|
||||
private final UmsAddressService addressService;
|
||||
|
||||
@Override
|
||||
public IPage<UmsMember> list(Page<UmsMember> page, String nickname) {
|
||||
|
18
pom.xml
18
pom.xml
@ -65,9 +65,6 @@
|
||||
<nimbus-jose-jwt.version>9.16.1</nimbus-jose-jwt.version>
|
||||
<thumbnailator.version>0.4.17</thumbnailator.version>
|
||||
|
||||
<!-- distributed -->
|
||||
<redisson.version>3.21.0</redisson.version>
|
||||
|
||||
<!-- 阿里云短信 -->
|
||||
<aliyun.java.sdk.core.version>4.5.25</aliyun.java.sdk.core.version>
|
||||
<aliyun.java.sdk.dysmsapi.version>2.1.0</aliyun.java.sdk.dysmsapi.version>
|
||||
@ -75,6 +72,11 @@
|
||||
<!-- minio -->
|
||||
<minio.version>8.5.3</minio.version>
|
||||
<okhttp3.version>4.8.1</okhttp3.version>
|
||||
|
||||
<aliyun-sdk-oss.version>3.16.3</aliyun-sdk-oss.version>
|
||||
|
||||
<!-- redisson 分布式锁 -->
|
||||
<redisson.version>3.21.0</redisson.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -157,10 +159,10 @@
|
||||
<version>${weixin-java.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 分布式锁 -->
|
||||
<!-- redisson 分布式锁 -->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
@ -315,6 +317,12 @@
|
||||
<version>${authorization-server.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>${aliyun-sdk-oss.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -1,120 +0,0 @@
|
||||
package com.youlai.auth.authentication.captcha;
|
||||
|
||||
import com.youlai.auth.authentication.password.ResourceOwnerPasswordAuthenticationToken;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 参数解析
|
||||
*
|
||||
* @see org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter
|
||||
*/
|
||||
public class CaptchaAuthenticationConverter implements AuthenticationConverter {
|
||||
|
||||
|
||||
public static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||
|
||||
|
||||
@Override
|
||||
public Authentication convert(HttpServletRequest request) {
|
||||
// grant_type (REQUIRED)
|
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
|
||||
if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MultiValueMap<String, String> parameters = getParameters(request);
|
||||
|
||||
// scope (OPTIONAL)
|
||||
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
|
||||
if (StringUtils.hasText(scope) &&
|
||||
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
|
||||
throwError(
|
||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
OAuth2ParameterNames.SCOPE,
|
||||
ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Set<String> requestedScopes = null;
|
||||
if (StringUtils.hasText(scope)) {
|
||||
requestedScopes = new HashSet<>(
|
||||
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
|
||||
}
|
||||
|
||||
// username (REQUIRED)
|
||||
String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
|
||||
if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
|
||||
throwError(
|
||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
OAuth2ParameterNames.USERNAME,
|
||||
ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
// password (REQUIRED)
|
||||
String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);
|
||||
if (!StringUtils.hasText(password) || parameters.get(OAuth2ParameterNames.PASSWORD).size() != 1) {
|
||||
throwError(
|
||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
OAuth2ParameterNames.PASSWORD,
|
||||
ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (clientPrincipal == null) {
|
||||
throwError(
|
||||
OAuth2ErrorCodes.INVALID_REQUEST,
|
||||
OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
ACCESS_TOKEN_REQUEST_ERROR_URI);
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = parameters
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) &&
|
||||
!e.getKey().equals(OAuth2ParameterNames.SCOPE))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
|
||||
ResourceOwnerPasswordAuthenticationToken resourceOwnerPasswordAuthenticationToken =
|
||||
new ResourceOwnerPasswordAuthenticationToken(
|
||||
clientPrincipal,
|
||||
requestedScopes,
|
||||
additionalParameters
|
||||
);
|
||||
return resourceOwnerPasswordAuthenticationToken;
|
||||
}
|
||||
|
||||
|
||||
public static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
|
||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap(parameterMap.size());
|
||||
parameterMap.forEach((key, values) -> {
|
||||
for (String value : values) {
|
||||
parameters.add(key, value);
|
||||
}
|
||||
|
||||
});
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public static void throwError(String errorCode, String parameterName, String errorUri) {
|
||||
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
package com.youlai.auth.authentication.captcha;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.common.constant.SecurityConstants;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 验证码授权模式授权者
|
||||
*
|
||||
* @author <a href="mailto:xianrui0365@163.com">haoxr</a>
|
||||
* @since 2021/9/25
|
||||
*/
|
||||
public class CaptchaTokenGranter extends AbstractTokenGranter {
|
||||
|
||||
/**
|
||||
* 声明授权者 CaptchaTokenGranter 支持授权模式 captcha
|
||||
* 根据接口传值 grant_type = captcha 的值匹配到此授权者
|
||||
* 匹配逻辑详见下面的两个方法
|
||||
*
|
||||
* @see org.springframework.security.oauth2.provider.CompositeTokenGranter#grant(String, TokenRequest)
|
||||
* @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant(String, TokenRequest)
|
||||
*/
|
||||
private static final String GRANT_TYPE = "captcha";
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
public CaptchaTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager,
|
||||
StringRedisTemplate redisTemplate
|
||||
) {
|
||||
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
|
||||
Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
|
||||
|
||||
// 验证码校验逻辑
|
||||
String validateCode = parameters.get("verifyCode");
|
||||
String uuid = parameters.get("verifyCodeKey");
|
||||
|
||||
Assert.isTrue(StrUtil.isNotBlank(validateCode), "验证码不能为空");
|
||||
String validateCodeKey = SecurityConstants.VERIFY_CODE_KEY_PREFIX + uuid;
|
||||
|
||||
// 从缓存取出正确的验证码和用户输入的验证码比对
|
||||
String correctValidateCode = redisTemplate.opsForValue().get(validateCodeKey);
|
||||
Assert.isTrue(StrUtil.isNotBlank(correctValidateCode),"验证码已过期");
|
||||
Assert.isTrue(validateCode.equals(correctValidateCode),"您输入的验证码不正确");
|
||||
|
||||
// 验证码验证通过,删除 Redis 的验证码
|
||||
redisTemplate.delete(validateCodeKey);
|
||||
|
||||
String username = parameters.get("username");
|
||||
String password = parameters.get("password");
|
||||
|
||||
// 移除后续无用参数
|
||||
parameters.remove("password");
|
||||
parameters.remove("verifyCode");
|
||||
parameters.remove("verifyCodeKey");
|
||||
|
||||
// 和密码模式一样的逻辑
|
||||
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
|
||||
try {
|
||||
userAuth = this.authenticationManager.authenticate(userAuth);
|
||||
} catch (AccountStatusException var8) {
|
||||
throw new InvalidGrantException(var8.getMessage());
|
||||
} catch (BadCredentialsException var9) {
|
||||
throw new InvalidGrantException(var9.getMessage());
|
||||
}
|
||||
|
||||
|
||||
if (userAuth != null && userAuth.isAuthenticated()) {
|
||||
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
return new OAuth2Authentication(storedOAuth2Request, userAuth);
|
||||
} else {
|
||||
throw new InvalidGrantException("Could not authenticate user: " + username);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package com.youlai.auth.authentication.mobile;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.auth.userdetails.member.MemberUserDetailsService;
|
||||
import com.youlai.common.constant.SecurityConstants;
|
||||
import com.youlai.common.web.exception.BizException;
|
||||
import com.youlai.mall.ums.api.MemberFeignClient;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* 短信验证码认证授权提供者
|
||||
*
|
||||
* @author <a href="mailto:xianrui0365@163.com">haoxr</a>
|
||||
* @since 2021/9/25
|
||||
*/
|
||||
@Data
|
||||
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
private MemberFeignClient memberFeignClient;
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
|
||||
String mobile = (String) authenticationToken.getPrincipal();
|
||||
String code = (String) authenticationToken.getCredentials();
|
||||
|
||||
if (!code.equals("666666")) { // 666666 是后门,因为短信收费,正式环境删除这个if分支
|
||||
String codeKey = SecurityConstants.SMS_CODE_PREFIX + mobile;
|
||||
String correctCode = redisTemplate.opsForValue().get(codeKey);
|
||||
// 验证码比对
|
||||
if (StrUtil.isBlank(correctCode) || !code.equals(correctCode)) {
|
||||
throw new BizException("验证码不正确");
|
||||
}
|
||||
// 比对成功删除缓存的验证码
|
||||
redisTemplate.delete(codeKey);
|
||||
}
|
||||
UserDetails userDetails = ((MemberUserDetailsService) userDetailsService).loadUserByMobile(mobile);
|
||||
SmsCodeAuthenticationToken result = new SmsCodeAuthenticationToken(userDetails, authentication.getCredentials(), new HashSet<>());
|
||||
result.setDetails(authentication.getDetails());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.youlai.auth.authentication.mobile;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 手机验证码登录
|
||||
*
|
||||
* @author <a href="mailto:xianrui0365@163.com">haoxr</a>
|
||||
* @since 2021/10/5
|
||||
*/
|
||||
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 550L;
|
||||
private final Object principal;
|
||||
private Object credentials;
|
||||
|
||||
public SmsCodeAuthenticationToken(Object principal, Object credentials) {
|
||||
super((Collection) null);
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
public SmsCodeAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
public Object getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
|
||||
Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
|
||||
public void eraseCredentials() {
|
||||
super.eraseCredentials();
|
||||
this.credentials = null;
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
package com.youlai.auth.authentication.mobile;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AccountStatusException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 手机验证码授权者
|
||||
*
|
||||
* @author <a href="mailto:xianrui0365@163.com">haoxr</a>
|
||||
* @since 2021/9/25
|
||||
*/
|
||||
public class SmsCodeTokenGranter extends AbstractTokenGranter {
|
||||
|
||||
/**
|
||||
* 声明授权者 SmsCodeTokenGranter 支持授权模式 sms_code
|
||||
* 根据接口传值 grant_type = sms_code 的值匹配到此授权者
|
||||
* 匹配逻辑详见下面的两个方法
|
||||
*
|
||||
* @see org.springframework.security.oauth2.provider.CompositeTokenGranter#grant(String, TokenRequest)
|
||||
* @see org.springframework.security.oauth2.provider.token.AbstractTokenGranter#grant(String, TokenRequest)
|
||||
*/
|
||||
private static final String GRANT_TYPE = "sms_code";
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
public SmsCodeTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory, AuthenticationManager authenticationManager
|
||||
) {
|
||||
super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
|
||||
Map<String, String> parameters = new LinkedHashMap(tokenRequest.getRequestParameters());
|
||||
|
||||
String mobile = parameters.get("mobile"); // 手机号
|
||||
String code = parameters.get("code"); // 短信验证码
|
||||
|
||||
parameters.remove("code");
|
||||
|
||||
Authentication userAuth = new SmsCodeAuthenticationToken(mobile, code);
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
|
||||
try {
|
||||
userAuth = this.authenticationManager.authenticate(userAuth);
|
||||
} catch (AccountStatusException var8) {
|
||||
throw new InvalidGrantException(var8.getMessage());
|
||||
} catch (BadCredentialsException var9) {
|
||||
throw new InvalidGrantException(var9.getMessage());
|
||||
}
|
||||
|
||||
if (userAuth != null && userAuth.isAuthenticated()) {
|
||||
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
return new OAuth2Authentication(storedOAuth2Request, userAuth);
|
||||
} else {
|
||||
throw new InvalidGrantException("Could not authenticate user: " + mobile);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.auth.authentication.miniapp;
|
||||
package com.youlai.auth.authentication.wechat;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.auth.util.OAuth2EndpointUtils;
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.auth.authentication.miniapp;
|
||||
package com.youlai.auth.authentication.wechat;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.youlai.auth.authentication.password.ResourceOwnerPasswordAuthenticationToken;
|
||||
@ -9,7 +9,6 @@ import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.oauth2.core.*;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
@ -24,8 +23,6 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -45,7 +42,6 @@ public class WxMiniAppAuthenticationProvider implements AuthenticationProvider {
|
||||
private final MemberUserDetailsService memberUserDetailsService;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ResourceOwnerPasswordAuthenticationProviderNew} using the provided parameters.
|
||||
*
|
||||
@ -87,27 +83,15 @@ public class WxMiniAppAuthenticationProvider implements AuthenticationProvider {
|
||||
}
|
||||
|
||||
UserDetails userDetails = memberUserDetailsService.loadUserByWechatCode(code, encryptedData, iv);
|
||||
|
||||
UsernamePasswordAuthenticationToken principal = UsernamePasswordAuthenticationToken.authenticated(userDetails, null,
|
||||
UsernamePasswordAuthenticationToken usernamePasswordAuthentication = UsernamePasswordAuthenticationToken.authenticated(userDetails, null,
|
||||
userDetails.getAuthorities());
|
||||
|
||||
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
|
||||
|
||||
WxMiniAppAuthenticationToken wxMiniAppAuthenticationToken = new WxMiniAppAuthenticationToken(authorities,
|
||||
clientPrincipal, principal, user, additionalParameters, details, appid, code, openid);
|
||||
|
||||
|
||||
|
||||
|
||||
// 生成 access_token
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
||||
// @formatter:on
|
||||
.authorizationGrantType(WxMiniAppAuthenticationToken.WX_MINI_APP)
|
||||
.authorizationGrant(authenticationToken);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
@ -125,9 +109,11 @@ public class WxMiniAppAuthenticationProvider implements AuthenticationProvider {
|
||||
// @formatter:off
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrantType(WxMiniAppAuthenticationToken.WX_MINI_APP)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.auth.authentication.miniapp;
|
||||
package com.youlai.auth.authentication.wechat;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@ -12,7 +12,8 @@ import java.util.Map;
|
||||
* 微信小程序
|
||||
*
|
||||
* @author haoxr
|
||||
* @see OAuth2AuthorizationCodeAuthenticationToken
|
||||
* @see org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
* @see OAuth2AuthorizationGrantAuthenticationToken
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class WxMiniAppAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
|
||||
@ -33,10 +34,12 @@ public class WxMiniAppAuthenticationToken extends OAuth2AuthorizationGrantAuthen
|
||||
|
||||
|
||||
/**
|
||||
* Sub-class constructor.
|
||||
*
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param additionalParameters the additional parameters
|
||||
* @param clientPrincipal
|
||||
* @param additionalParameters
|
||||
* @param code
|
||||
* @param encryptedData
|
||||
* @param iv
|
||||
*/
|
||||
protected WxMiniAppAuthenticationToken(
|
||||
Authentication clientPrincipal,
|
||||
@ -49,5 +52,9 @@ public class WxMiniAppAuthenticationToken extends OAuth2AuthorizationGrantAuthen
|
||||
this.code = code;
|
||||
this.encryptedData = encryptedData;
|
||||
this.iv = iv;
|
||||
super.setAuthenticated(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.youlai.auth.authentication.miniapp;
|
||||
package com.youlai.auth.authentication.wechat;
|
||||
|
||||
/**
|
||||
* 微信小程序参数名称
|
||||
@ -15,5 +15,4 @@ public interface WxMiniAppParameterNames {
|
||||
|
||||
String ENCRYPTED_DATA = "encryptedData";
|
||||
|
||||
|
||||
}
|
@ -8,6 +8,7 @@ import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import com.youlai.auth.authentication.password.ResourceOwnerPasswordAuthenticationConverter;
|
||||
import com.youlai.auth.authentication.password.ResourceOwnerPasswordAuthenticationProvider;
|
||||
import com.youlai.auth.authentication.wechat.WxMiniAppAuthenticationToken;
|
||||
import com.youlai.auth.userdetails.user.SysUserDetails;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -144,34 +145,22 @@ public class AuthorizationServerConfig {
|
||||
|
||||
@Bean
|
||||
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
|
||||
String messagingClientId = "messaging-client";
|
||||
String messagingClientId = "mall-app";
|
||||
RegisteredClient messagingClient = RegisteredClient.withId(UUID.randomUUID().toString())
|
||||
.clientId(messagingClientId)
|
||||
.clientSecret("{noop}secret")
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
|
||||
.authorizationGrantType(WxMiniAppAuthenticationToken.WX_MINI_APP)
|
||||
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
|
||||
.redirectUri("http://127.0.0.1:8080/authorized")
|
||||
.postLogoutRedirectUri("http://127.0.0.1:8080/logged-out")
|
||||
.scope(OidcScopes.OPENID)
|
||||
.scope(OidcScopes.PROFILE)
|
||||
.scope("message.read")
|
||||
.scope("message.write")
|
||||
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
|
||||
.build();
|
||||
|
||||
String deviceClientId = "device-messaging-client";
|
||||
RegisteredClient deviceClient = RegisteredClient.withId(UUID.randomUUID().toString())
|
||||
.clientId(deviceClientId)
|
||||
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
|
||||
.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
|
||||
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
|
||||
.scope("message.read")
|
||||
.scope("message.write")
|
||||
.build();
|
||||
|
||||
// Save registered client's in db as if in-memory
|
||||
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
|
||||
|
||||
@ -180,14 +169,8 @@ public class AuthorizationServerConfig {
|
||||
registeredClientRepository.save(messagingClient);
|
||||
}
|
||||
|
||||
RegisteredClient registeredDeviceClient = registeredClientRepository.findByClientId(deviceClientId);
|
||||
if (registeredDeviceClient == null) {
|
||||
registeredClientRepository.save(deviceClient);
|
||||
}
|
||||
|
||||
return registeredClientRepository;
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
@Bean
|
||||
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
|
||||
|
@ -15,9 +15,9 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author haoxr
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "miniapp.wechat")
|
||||
@ConfigurationProperties(prefix = "wx.miniapp")
|
||||
@Configuration
|
||||
public class WechatMiniappConfig {
|
||||
public class WxMiniAppConfig {
|
||||
|
||||
@Setter
|
||||
private String appid;
|
@ -27,18 +27,13 @@ import org.springframework.stereotype.Service;
|
||||
* @author haoxr
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Service("memberUserDetailsService")
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MemberUserDetailsService implements UserDetailsService {
|
||||
public class MemberUserDetailsService {
|
||||
|
||||
private final MemberFeignClient memberFeignClient;
|
||||
private final WxMaService wxMaService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 手机号码认证方式
|
||||
@ -97,6 +92,9 @@ public class MemberUserDetailsService implements UserDetailsService {
|
||||
}
|
||||
} else {
|
||||
Assert.isTrue((memberAuthInfo = getMemberAuthInfoResult.getData()) != null, "获取会员认证信息失败");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 用户不存在
|
||||
|
@ -3,17 +3,21 @@ server:
|
||||
|
||||
spring:
|
||||
mvc:
|
||||
pathmatch:
|
||||
path-match:
|
||||
matching-strategy: ant_path_matcher
|
||||
cloud:
|
||||
nacos:
|
||||
# 注册中心
|
||||
discovery:
|
||||
server-addr: http://localhost:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
# 配置中心
|
||||
config:
|
||||
server-addr: http://f.youlai.tech:8848
|
||||
server-addr: http://localhost:8848
|
||||
file-extension: yaml
|
||||
shared-configs[0]:
|
||||
data-id: youlai-common.yaml
|
||||
refresh: true
|
||||
username: nacos
|
||||
password: nacos
|
@ -24,12 +24,8 @@ public class NamedThreadFactory implements ThreadFactory {
|
||||
public final String namePrefix;
|
||||
|
||||
public NamedThreadFactory(String name) {
|
||||
SecurityManager securityManager = System.getSecurityManager();
|
||||
if (securityManager != null) {
|
||||
this.threadGroup = securityManager.getThreadGroup();
|
||||
} else {
|
||||
|
||||
this.threadGroup = Thread.currentThread().getThreadGroup();
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(name)) {
|
||||
name = "pool";
|
||||
|
@ -29,5 +29,10 @@
|
||||
<artifactId>minio</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
@ -8,8 +8,8 @@ import com.aliyun.oss.OSS;
|
||||
import com.aliyun.oss.OSSClientBuilder;
|
||||
import com.aliyun.oss.model.ObjectMetadata;
|
||||
import com.aliyun.oss.model.PutObjectRequest;
|
||||
import com.youlai.system.model.dto.FileInfo;
|
||||
import com.youlai.system.service.OssService;
|
||||
import com.youlai.common.file.model.FileInfo;
|
||||
import com.youlai.common.file.service.OssService;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -65,7 +65,7 @@ public class AliyunOssService implements OssService {
|
||||
// 生成文件名(日期文件夹)
|
||||
String suffix = FileUtil.getSuffix(file.getOriginalFilename());
|
||||
String uuid = IdUtil.simpleUUID();
|
||||
String fileName = DateUtil.format(LocalDateTime.now(), "yyyy/MM/dd") + "/" + uuid + "." + suffix;
|
||||
String fileName = DateUtil.format(LocalDateTime.now(), "yyyyMMdd") + "/" + uuid + "." + suffix;
|
||||
// try-with-resource 语法糖自动释放流
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
|
||||
|
@ -5,8 +5,8 @@ import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.system.model.dto.FileInfo;
|
||||
import com.youlai.system.service.OssService;
|
||||
import com.youlai.common.file.model.FileInfo;
|
||||
import com.youlai.common.file.service.OssService;
|
||||
import io.minio.*;
|
||||
import io.minio.errors.*;
|
||||
import io.minio.http.Method;
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<!--1. 输出到控制台-->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<withJansi>true</withJansi>
|
||||
<!-- <withJansi>true</withJansi>-->
|
||||
<!--此日志appender是为开发使用,只配置最低级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
@ -30,11 +30,11 @@
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} -%5level ---[%15.15thread] %-40.40logger{39} : %msg%n%n</pattern>
|
||||
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
|
||||
</encoder>
|
||||
<!-- 日志记录器的滚动策略,按日期、大小记录 -->
|
||||
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>20MB</maxFileSize>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<!--日志文档保留天数-->
|
||||
<maxHistory>15</maxHistory>
|
||||
@ -47,9 +47,9 @@
|
||||
|
||||
<!-- 开发环境输出至控制台 -->
|
||||
<springProfile name="dev">
|
||||
<logger name="com.alibaba.nacos" level="OFF" addtivity="false"></logger>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
@ -60,13 +60,4 @@
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<!-- k8s环境输出至文件 -->
|
||||
<springProfile name="k8s">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
@ -32,11 +32,6 @@
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
@ -48,5 +43,11 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- redisson 分布式锁 -->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,33 +1,35 @@
|
||||
package com.youlai.common.redis;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
|
||||
|
||||
/**
|
||||
* Redis 配置类
|
||||
* Redis 配置
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2023/5/15
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore(RedisAutoConfiguration.class)
|
||||
public class RedisConfig {
|
||||
|
||||
|
||||
/**
|
||||
* 自定义序列化
|
||||
* 自定义 RedisTemplate
|
||||
* <p>
|
||||
* 修改 Redis 序列化方式,默认 JdkSerializationRedisSerializer
|
||||
*
|
||||
* @param lettuceConnectionFactory
|
||||
* @return
|
||||
* @param redisConnectionFactory {@link RedisConnectionFactory}
|
||||
* @return {@link RedisTemplate}
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
||||
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
|
||||
redisTemplate.setKeySerializer(RedisSerializer.string());
|
||||
redisTemplate.setValueSerializer(RedisSerializer.json());
|
||||
@ -39,4 +41,5 @@ public class RedisConfig {
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,48 +0,0 @@
|
||||
package com.youlai.common.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Setter;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
import org.redisson.config.SingleServerConfig;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 分布式锁 Redisson 配置
|
||||
*
|
||||
* @author huawei
|
||||
* @email huawei_code@163.com
|
||||
* @since 2021/2/22
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "redisson",name = "address")
|
||||
@ConfigurationProperties(prefix = "redisson")
|
||||
@Configuration
|
||||
public class RedissonConfig {
|
||||
|
||||
@Setter
|
||||
private String address;
|
||||
@Setter
|
||||
private String password;
|
||||
@Setter
|
||||
private Integer database;
|
||||
@Setter
|
||||
private Integer minIdle;// 默认最小空闲连接数
|
||||
|
||||
@Bean
|
||||
public RedissonClient redissonClient() {
|
||||
Config config = new Config();
|
||||
SingleServerConfig singleServerConfig = config.useSingleServer();
|
||||
singleServerConfig.setAddress(address);
|
||||
singleServerConfig.setDatabase(database);
|
||||
singleServerConfig.setConnectionMinimumIdleSize(minIdle);
|
||||
if (StrUtil.isNotBlank(password)) {
|
||||
singleServerConfig.setPassword(password);
|
||||
}
|
||||
return Redisson.create(config);
|
||||
}
|
||||
|
||||
}
|
@ -20,12 +20,12 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-web</artifactId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -3,7 +3,6 @@ package com.youlai.common.security.config;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ -11,17 +10,14 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -29,14 +25,9 @@ import java.util.List;
|
||||
@ConfigurationProperties(prefix = "security")
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ResourceServerConfig {
|
||||
|
||||
private final AccessDeniedHandler accessDeniedHandler;
|
||||
private final AuthenticationEntryPoint authenticationEntryPoint;
|
||||
|
||||
@Setter
|
||||
private List<String> ignoreUrls;
|
||||
|
||||
@ -44,25 +35,29 @@ public class ResourceServerConfig {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
if (CollectionUtil.isEmpty(ignoreUrls)) {
|
||||
ignoreUrls = Arrays.asList("/webjars/**", "/doc.html", "/swagger-resources/**", "/v2/api-docs");
|
||||
ignoreUrls = Arrays.asList(
|
||||
"/webjars/**",
|
||||
"/doc.html",
|
||||
"/swagger-resources/**",
|
||||
"/v3/api-docs/**",
|
||||
"/swagger-ui/**"
|
||||
);
|
||||
}
|
||||
|
||||
log.info("whitelist path:{}", JSONUtil.toJsonStr(ignoreUrls));
|
||||
|
||||
http
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(Convert.toStrArray(ignoreUrls)).permitAll()
|
||||
http.authorizeHttpRequests(requestMatcherRegistry ->
|
||||
requestMatcherRegistry.requestMatchers(Convert.toStrArray(ignoreUrls)).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
;
|
||||
http.oauth2ResourceServer()
|
||||
.jwt()
|
||||
.jwtAuthenticationConverter(jwtAuthenticationConverter())
|
||||
.and()
|
||||
http.oauth2ResourceServer(resourceServerConfigurer ->
|
||||
resourceServerConfigurer.jwt(jwtConfigurer -> jwtAuthenticationConverter())
|
||||
)
|
||||
/*.and()
|
||||
.authenticationEntryPoint(authenticationEntryPoint)
|
||||
.accessDeniedHandler(accessDeniedHandler)
|
||||
.accessDeniedHandler(accessDeniedHandler)*/
|
||||
;
|
||||
return http.build();
|
||||
}
|
||||
@ -70,7 +65,7 @@ public class ResourceServerConfig {
|
||||
/**
|
||||
* 自定义JWT Converter
|
||||
*
|
||||
* @return
|
||||
* @return Converter
|
||||
* @see JwtAuthenticationProvider#setJwtAuthenticationConverter(Converter)
|
||||
*/
|
||||
public Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter() {
|
||||
@ -83,4 +78,5 @@ public class ResourceServerConfig {
|
||||
return jwtAuthenticationConverter;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ package com.youlai.common.security.exception;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.result.ResultCode;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
@ -3,21 +3,19 @@ package com.youlai.common.security.exception;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.result.ResultCode;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* token 无效自定义异常
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/11/13
|
||||
* @date 2022/11/13
|
||||
*/
|
||||
@Component
|
||||
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
@ -2,8 +2,10 @@ package com.youlai.common.security.service;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.common.constant.SecurityConstants;
|
||||
import com.youlai.common.security.util.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
@ -11,34 +13,49 @@ import org.springframework.util.PatternMatchUtils;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Spring Security 自定义权限校验
|
||||
* SpringSecurity权限校验
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/10/31
|
||||
* @since 2022/2/22
|
||||
*/
|
||||
@Service("pms")
|
||||
@Service("ss")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class PermissionService {
|
||||
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
public boolean hasPermission(String perm) {
|
||||
/**
|
||||
* 判断当前登录用户是否拥有操作权限
|
||||
*
|
||||
* @param perm 权限标识(eg: sys:user:add)
|
||||
* @return
|
||||
*/
|
||||
public boolean hasPerm(String perm) {
|
||||
|
||||
if (StrUtil.isBlank(perm)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 超级管理员放行
|
||||
if (SecurityUtils.isRoot()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
|
||||
Set<String> perms = (Set<String>) redisTemplate.opsForValue().get("AUTH:USER_PERMS:" + userId);
|
||||
Set<String> perms = (Set<String>) redisTemplate.opsForValue().get(SecurityConstants.USER_PERMS_CACHE_PREFIX + userId); // 权限数据用户登录成功节点存入redis,详见 JwtTokenManager#createToken()
|
||||
|
||||
if (CollectionUtil.isEmpty(perms)) {
|
||||
return false;
|
||||
}
|
||||
return perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
|
||||
boolean hasPermission = perms.stream()
|
||||
.anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item)); // *号匹配任意字符
|
||||
|
||||
if (!hasPermission) {
|
||||
log.error("用户无访问权限");
|
||||
}
|
||||
return hasPermission;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,115 +13,60 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Spring Security Context工具类
|
||||
* <p>
|
||||
* 获取登录用户信息(用户名、角色、部门)
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/10/29
|
||||
*/
|
||||
public class SecurityUtils {
|
||||
|
||||
/**
|
||||
* 获取用户ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Long getUserId() {
|
||||
Long userId = Convert.toLong(getTokenAttributes().get("userId"));
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Long getMemberId() {
|
||||
Long userId = Convert.toLong(getTokenAttributes().get("memberId"));
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户登录名
|
||||
*
|
||||
* @return username
|
||||
*/
|
||||
public static String getUsername() {
|
||||
String username = Convert.toStr(getTokenAttributes().get("username"));
|
||||
return username;
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return authentication.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户昵称/姓名
|
||||
*
|
||||
* @return nickname
|
||||
*/
|
||||
public static String getNickname() {
|
||||
String nickname = Convert.toStr(getTokenAttributes().get("nickname"));
|
||||
return nickname;
|
||||
public static Map<String, Object> getTokenAttributes() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
|
||||
return jwtAuthenticationToken.getTokenAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色
|
||||
*
|
||||
* @return 角色Code集合
|
||||
*/
|
||||
|
||||
public static Set<String> getRoles() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null && CollectionUtil.isNotEmpty(authentication.getAuthorities())) {
|
||||
Set<String> roles = authentication.getAuthorities()
|
||||
|
||||
Set roles;
|
||||
if (CollectionUtil.isNotEmpty(authentication.getAuthorities())) {
|
||||
roles = authentication.getAuthorities()
|
||||
.stream()
|
||||
.map(item -> StrUtil.removePrefix(item.getAuthority(), "ROLE_"))
|
||||
.collect(Collectors.toSet());
|
||||
return roles;
|
||||
.map(item -> StrUtil.removePrefix(item.getAuthority(), "ROLE_")).collect(Collectors.toSet());
|
||||
} else {
|
||||
roles = Collections.EMPTY_SET;
|
||||
}
|
||||
return Collections.EMPTY_SET;
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门ID
|
||||
*
|
||||
* @return deptId
|
||||
*/
|
||||
public static Long getDeptId() {
|
||||
Long deptId = Convert.toLong(getTokenAttributes().get("deptId"));
|
||||
return deptId;
|
||||
return Convert.toLong(getTokenAttributes().get("deptId"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限
|
||||
*
|
||||
* @return DataScope
|
||||
*/
|
||||
public static Integer getDataScope() {
|
||||
Integer dataScope = Convert.toInt(getTokenAttributes().get("dataScope"));
|
||||
return dataScope;
|
||||
public static boolean isRoot() {
|
||||
return getRoles().contains(GlobalConstants.ROOT_ROLE_CODE);
|
||||
}
|
||||
|
||||
public static String getJti() {
|
||||
return String.valueOf(getTokenAttributes().get("jti"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否为超级管理员
|
||||
*
|
||||
* @return true/false
|
||||
*/
|
||||
public static boolean isRoot() {
|
||||
return getRoles().contains(GlobalConstants.ROOT_ROLE_CODE);
|
||||
public static Integer getDataScope() {
|
||||
return Convert.toInt(getTokenAttributes().get("dataScope"));
|
||||
}
|
||||
|
||||
public static Map<String, Object> getTokenAttributes() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication != null) {
|
||||
if (authentication instanceof JwtAuthenticationToken) {
|
||||
JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
|
||||
Map<String, Object> tokenAttributes = jwtAuthenticationToken.getTokenAttributes();
|
||||
return tokenAttributes;
|
||||
}
|
||||
}
|
||||
return Collections.EMPTY_MAP;
|
||||
public static Long getMemberId() {
|
||||
|
||||
return Convert.toLong(getTokenAttributes().get("memberId"));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
com.youlai.common.web.config.WebMvcConfig
|
||||
com.youlai.common.web.config.FeignConfig
|
||||
com.youlai.common.web.exception.GlobalExceptionHandler
|
||||
com.youlai.common.web.annotation.PreventDuplicateResubmit
|
||||
com.youlai.common.web.aspect.DuplicateSubmitAspect
|
@ -1,74 +0,0 @@
|
||||
package com.youlai.gateway.config;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.reactive.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* OAuth Client Security 配置
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/8/28
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "security")
|
||||
@EnableWebFluxSecurity
|
||||
@Slf4j
|
||||
public class OAuth2ClientSecurityConfig {
|
||||
|
||||
/**
|
||||
* 禁用访问路径集合
|
||||
*/
|
||||
@Setter
|
||||
private List<String> forbiddenURIs;
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http
|
||||
) {
|
||||
if (CollectionUtil.isEmpty(forbiddenURIs)) {
|
||||
forbiddenURIs = Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
http.authorizeExchange()
|
||||
.pathMatchers(Convert.toStrArray(forbiddenURIs)).denyAll()
|
||||
// 放行交由资源服务器进行认证鉴权
|
||||
.anyExchange().permitAll()
|
||||
.and()
|
||||
// 禁用csrf token安全校验
|
||||
.csrf().disable();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
|
||||
CorsConfiguration corsConfig = new CorsConfiguration();
|
||||
|
||||
// 允许所有请求方法
|
||||
corsConfig.addAllowedMethod("*");
|
||||
// 允许所有域,当请求头
|
||||
corsConfig.addAllowedOriginPattern("*");
|
||||
// 允许全部请求头
|
||||
corsConfig.addAllowedHeader("*");
|
||||
// 允许携带 Authorization 头
|
||||
corsConfig.setAllowCredentials(true);
|
||||
// 允许全部请求路径
|
||||
source.registerCorsConfiguration("/**", corsConfig);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.youlai.gateway.config;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Security 安全配置
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/8/28
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = "security")
|
||||
public class SecurityConfig {
|
||||
|
||||
@Setter
|
||||
private List<String> forbiddenURIs;
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeExchange(exchangeSpec ->
|
||||
exchangeSpec
|
||||
.pathMatchers(Convert.toStrArray(forbiddenURIs)).denyAll()
|
||||
.anyExchange().permitAll()
|
||||
)
|
||||
.csrf(ServerHttpSecurity.CsrfSpec::disable);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package com.youlai.gateway.swagger.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Component;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author haoxr
|
||||
* @since 2022/5/17 16:21
|
||||
*/
|
||||
|
||||
@Component
|
||||
@Primary
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
|
||||
|
||||
private final RouteLocator routeLocator;
|
||||
private final GatewayProperties gatewayProperties;
|
||||
|
||||
@Override
|
||||
public List<SwaggerResource> get() {
|
||||
List<SwaggerResource> resources = new ArrayList<>();
|
||||
List<String> routes = new ArrayList<>();
|
||||
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
|
||||
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
|
||||
route.getPredicates().stream()
|
||||
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
|
||||
.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
|
||||
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
|
||||
.replace("**", "v2/api-docs"))));
|
||||
});
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
private SwaggerResource swaggerResource(String name, String location) {
|
||||
log.info("name:{},location:{}",name,location);
|
||||
SwaggerResource swaggerResource = new SwaggerResource();
|
||||
swaggerResource.setName(name);
|
||||
swaggerResource.setLocation(location);
|
||||
swaggerResource.setSwaggerVersion("2.0");
|
||||
return swaggerResource;
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package com.youlai.gateway.swagger.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
|
||||
/**
|
||||
* 聚合各个服务的swagger接口
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/5/17 0:53
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SwaggerResourceHandler implements HandlerFunction<ServerResponse> {
|
||||
|
||||
private final SwaggerResourcesProvider swaggerResources;
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
Mono<ServerResponse> responseMono = ServerResponse.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters
|
||||
.fromValue(swaggerResources.get()));
|
||||
return responseMono;
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package com.youlai.gateway.swagger.handler;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.SecurityConfiguration;
|
||||
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
/**
|
||||
* 权限处理器
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/5/17
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerSecurityHandler implements HandlerFunction<ServerResponse> {
|
||||
|
||||
@Autowired(required = false)
|
||||
private SecurityConfiguration securityConfiguration;
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
Mono<ServerResponse> responseMono = ServerResponse.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters
|
||||
.fromValue(Optional.ofNullable(securityConfiguration)
|
||||
.orElse(SecurityConfigurationBuilder.builder().build())));
|
||||
return responseMono;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package com.youlai.gateway.swagger.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.UiConfiguration;
|
||||
import springfox.documentation.swagger.web.UiConfigurationBuilder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
/**
|
||||
* UI处理器
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/5/17 0:51
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerUiHandler implements HandlerFunction<ServerResponse> {
|
||||
|
||||
|
||||
@Autowired(required = false)
|
||||
private UiConfiguration uiConfiguration;
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
Mono<ServerResponse> responseMono = ServerResponse.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(BodyInserters
|
||||
.fromValue(Optional.ofNullable(uiConfiguration)
|
||||
.orElse(UiConfigurationBuilder.builder().build())));
|
||||
return responseMono;
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package com.youlai.gateway.swagger.router;
|
||||
|
||||
import com.youlai.gateway.swagger.handler.SwaggerResourceHandler;
|
||||
import com.youlai.gateway.swagger.handler.SwaggerSecurityHandler;
|
||||
import com.youlai.gateway.swagger.handler.SwaggerUiHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
/**
|
||||
* Swagger路由
|
||||
*
|
||||
* @author haoxr
|
||||
* @since 2022/5/16 16:34
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class SwaggerRouter {
|
||||
|
||||
/**
|
||||
* 聚合各个服务的swagger接口
|
||||
*/
|
||||
private final SwaggerResourceHandler swaggerResourceHandler;
|
||||
/**
|
||||
* 权限处理器
|
||||
*/
|
||||
private final SwaggerSecurityHandler swaggerSecurityHandler;
|
||||
/**
|
||||
* UI处理器
|
||||
*/
|
||||
private final SwaggerUiHandler swaggerUiHandler;
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> swaggerRouterFunction() {
|
||||
return RouterFunctions
|
||||
.route(RequestPredicates.GET("/swagger-resources/configuration/security").and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler::handle)
|
||||
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui").and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler::handle)
|
||||
.andRoute(RequestPredicates.GET("/swagger-resources").and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler::handle);
|
||||
}
|
||||
|
||||
}
|
@ -3,9 +3,12 @@ package com.youlai.system.controller;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.common.result.PageResult;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.web.resubmit.Resubmit;
|
||||
import com.youlai.common.web.annotation.PreventDuplicateResubmit;
|
||||
import com.youlai.common.web.model.Option;
|
||||
import com.youlai.system.model.form.RoleForm;
|
||||
import com.youlai.system.model.query.RolePageQuery;
|
||||
import com.youlai.system.model.vo.RolePageVO;
|
||||
import com.youlai.system.service.SysRoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
|
@ -180,8 +180,7 @@ public class SysUserController {
|
||||
|
||||
public static <T> String importExcel(InputStream is, Class clazz, MyAnalysisEventListener<T> listener) {
|
||||
EasyExcel.read(is, clazz, listener).sheet().doRead();
|
||||
String msg = listener.getMsg();
|
||||
return msg;
|
||||
return listener.getMsg();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.youlai.system.handler;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.youlai.system.model.vo.user.UserLoginVO;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.system.model.vo.UserInfoVO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
@ -18,8 +18,8 @@ public class UserBlockHandler {
|
||||
* @param blockException
|
||||
* @return
|
||||
*/
|
||||
public static Result<UserLoginVO> handleGetCurrentUserBlock(BlockException blockException) {
|
||||
return Result.success(new UserLoginVO());
|
||||
public static Result<UserInfoVO> handleGetCurrentUserBlock(BlockException blockException) {
|
||||
return Result.success(new UserInfoVO());
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ public class UserExportVO {
|
||||
private String email;
|
||||
|
||||
@ExcelProperty(value = "创建时间")
|
||||
@sinceTimeFormat("yyyy/MM/dd HH:mm:ss")
|
||||
@DateTimeFormat("yyyy/MM/dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
|
||||
|
@ -88,8 +88,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
);
|
||||
|
||||
// 实体转换
|
||||
List<Option> list = roleConverter.entities2Options(roleList);
|
||||
return list;
|
||||
return roleConverter.entities2Options(roleList);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,8 +113,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
// 实体转换
|
||||
SysRole role = roleConverter.form2Entity(roleForm);
|
||||
|
||||
boolean result = this.saveOrUpdate(role);
|
||||
return result;
|
||||
return this.saveOrUpdate(role);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,8 +162,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
});
|
||||
|
||||
|
||||
boolean result = this.removeByIds(roleIds);
|
||||
return result;
|
||||
return this.removeByIds(roleIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,8 +173,7 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
|
||||
*/
|
||||
@Override
|
||||
public List<Long> getRoleMenuIds(Long roleId) {
|
||||
List<Long> menuIds = sysRoleMenuService.listMenuIdsByRoleId(roleId);
|
||||
return menuIds;
|
||||
return sysRoleMenuService.listMenuIdsByRoleId(roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,21 +0,0 @@
|
||||
package com.youlai.system.service.impl;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
|
||||
/**
|
||||
* @author haoxr
|
||||
* @since 2021/8/28
|
||||
*/
|
||||
@SpringBootTest
|
||||
class SysUserServiceImplTest {
|
||||
|
||||
@Autowired
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Test
|
||||
public void saveUser() {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user