diff --git a/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/api/MemberFeignClient.java b/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/api/MemberFeignClient.java index d1feef424c26310925b72ed27f5aebd81f6d2962..65a758f00abd864e8b7ced4b026eb22c7f853b0d 100644 --- a/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/api/MemberFeignClient.java +++ b/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/api/MemberFeignClient.java @@ -29,22 +29,40 @@ public interface MemberFeignClient { @GetMapping("/app-api/v1/members/detail/{id}") Result getUserEntityById(@PathVariable Long id); - /** - * 获取认证会员信息 - */ - @GetMapping("/app-api/v1/members/openid/{openid}") - Result loadUserByOpenId(@PathVariable String openid); /** * 扣减会员余额 */ @PutMapping("/app-api/v1/members/current/balances/_deduct") - Result deductBalance( @RequestParam Long balances); + Result deductBalance(@RequestParam Long balances); + /** * 添加浏览记录 */ @PostMapping("/app-api/v1/members/view/history") Result addProductViewHistory(@RequestBody ProductHistoryVO product); + + + /** + * 根据openId获取会员认证信息 + * + * @param openid + * @return + */ + @GetMapping("/app-api/v1/members/openid/{openid}") + Result loadUserByOpenId(@PathVariable String openid); + + + /** + * 根据手机号获取会员认证信息 + * + * @param mobile + * @return + */ + @GetMapping("/app-api/v1/members/mobile/{mobile}") + Result loadUserByMobile(String mobile); + + } diff --git a/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/pojo/dto/MemberAuthDTO.java b/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/pojo/dto/MemberAuthDTO.java index c4f2b4e9209c980ae34aec5480a7a427eb3a92dc..b931258f34b65087584835f409e64b37682a4f90 100644 --- a/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/pojo/dto/MemberAuthDTO.java +++ b/mall-ums/ums-api/src/main/java/com/youlai/mall/ums/pojo/dto/MemberAuthDTO.java @@ -8,6 +8,6 @@ import lombok.Data; public class MemberAuthDTO { private Long userId; - private String openId; + private String username; private Integer status; } diff --git a/mall-ums/ums-boot/src/main/java/com/youlai/mall/ums/controller/app/MemberController.java b/mall-ums/ums-boot/src/main/java/com/youlai/mall/ums/controller/app/MemberController.java index 3e0694e70cf8c9fe25eb420041861bd9654c205e..540d01550586b9815db494dcd3632dedc4f45e1e 100644 --- a/mall-ums/ums-boot/src/main/java/com/youlai/mall/ums/controller/app/MemberController.java +++ b/mall-ums/ums-boot/src/main/java/com/youlai/mall/ums/controller/app/MemberController.java @@ -61,20 +61,6 @@ public class MemberController { return Result.success(user); } - @ApiOperation(value = "根据openid获取会员认证信息") - @ApiImplicitParam(name = "openid", value = "微信身份唯一标识", required = true, paramType = "path", dataType = "String") - @GetMapping("/openid/{openid}") - public Result getByOpenid(@PathVariable String openid) { - UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper().eq(UmsMember::getOpenid, openid) - .select(UmsMember::getId,UmsMember::getOpenid,UmsMember::getStatus) - ); - if (member == null) { - return Result.failed(ResultCode.USER_NOT_EXIST); - } - // 会员认证信息 - MemberAuthDTO memberAuth = new MemberAuthDTO(member.getId(),member.getOpenid(),member.getStatus()); - return Result.success(memberAuth); - } @ApiOperation(value = "新增会员") @ApiImplicitParam(name = "member", value = "实体JSON对象", required = true, paramType = "body", dataType = "UmsMember") @@ -153,4 +139,36 @@ public class MemberController { return Result.success(Collections.emptySet()); } } + + @ApiOperation(value = "根据openid获取会员认证信息") + @ApiImplicitParam(name = "openid", value = "微信身份唯一标识", required = true, paramType = "path", dataType = "String") + @GetMapping("/openid/{openid}") + public Result getByOpenid(@PathVariable String openid) { + UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper() + .eq(UmsMember::getOpenid, openid) + .select(UmsMember::getId, UmsMember::getOpenid, UmsMember::getStatus) + ); + if (member == null) { + return Result.failed(ResultCode.USER_NOT_EXIST); + } + MemberAuthDTO memberAuth = new MemberAuthDTO(member.getId(), member.getOpenid(), member.getStatus()); + return Result.success(memberAuth); + } + + @ApiOperation(value = "根据手机号获取会员认证信息") + @ApiImplicitParam(name = "mobile", value = "会员手机号码", required = true, paramType = "path", dataType = "String") + @GetMapping("/mobile/{mobile}") + public Result getByMobile(@PathVariable String mobile) { + UmsMember member = iUmsMemberService.getOne(new LambdaQueryWrapper() + .eq(UmsMember::getMobile, mobile) + .select(UmsMember::getId, UmsMember::getMobile, UmsMember::getStatus) + ); + if (member == null) { + return Result.failed(ResultCode.USER_NOT_EXIST); + } + MemberAuthDTO memberAuth = new MemberAuthDTO(member.getId(), member.getMobile(), member.getStatus()); + return Result.success(memberAuth); + } + + } diff --git a/pom.xml b/pom.xml index 7769d767b71802dd02481fe565ec3f5cdb38a239..3c855c4ae7a1f4c71581621c84557c5c8d821b96 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 1.6.2 youlai - 1.4.13 + 2.3.2 @@ -263,6 +263,12 @@ common-log ${youlai.version} + + + com.github.penggle + kaptcha + ${kaptcha.version} + diff --git a/youlai-admin/admin-api/src/main/java/com/youlai/admin/pojo/dto/UserAuthDTO.java b/youlai-admin/admin-api/src/main/java/com/youlai/admin/pojo/dto/UserAuthDTO.java index 617fb8315d43a079eaa673548aa4f19a6f3ca93c..8942c0389ad51070fe8558b1b1b31e517d7d8eb9 100644 --- a/youlai-admin/admin-api/src/main/java/com/youlai/admin/pojo/dto/UserAuthDTO.java +++ b/youlai-admin/admin-api/src/main/java/com/youlai/admin/pojo/dto/UserAuthDTO.java @@ -38,4 +38,6 @@ public class UserAuthDTO { */ private List roles; + + } diff --git a/youlai-auth/pom.xml b/youlai-auth/pom.xml index b107e8a47bdda279abbdd2385e4b671fea2af686..6157e290d5fa53f5efad5f688503aa295df9ac5e 100644 --- a/youlai-auth/pom.xml +++ b/youlai-auth/pom.xml @@ -102,6 +102,7 @@ com.github.xiaoymin knife4j-micro-spring-boot-starter + diff --git a/youlai-auth/src/main/java/com/youlai/auth/common/constant/OAuthConstants.java b/youlai-auth/src/main/java/com/youlai/auth/common/constant/AuthConstants.java similarity index 59% rename from youlai-auth/src/main/java/com/youlai/auth/common/constant/OAuthConstants.java rename to youlai-auth/src/main/java/com/youlai/auth/common/constant/AuthConstants.java index 8dd643357769453acb1e16bc54c43dc90b751198..8105ea2aaf4163765ecd1eb1a056d58001ebae03 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/common/constant/OAuthConstants.java +++ b/youlai-auth/src/main/java/com/youlai/auth/common/constant/AuthConstants.java @@ -4,8 +4,12 @@ package com.youlai.auth.common.constant; * @author xianrui * @date 2021/9/30 */ -public interface OAuthConstants { +public interface AuthConstants { String TEST_CLIENT_ID = "test"; + String ADMIN_CLIENT_ID = "mall-admin"; + + String APP_CLIENT_ID = "mall-app"; + } diff --git a/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtGenerator.java b/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtGenerator.java deleted file mode 100644 index d458b548ab6405e077750754601eda4aa40586c7..0000000000000000000000000000000000000000 --- a/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.youlai.auth.common.jwt; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.jwt.JwtHelper; -import org.springframework.security.jwt.crypto.sign.RsaSigner; -import org.springframework.stereotype.Component; - -import java.security.KeyPair; -import java.security.interfaces.RSAPrivateKey; -import java.util.Map; -import java.util.Set; - -/** - * 描述: [类型描述] - * 创建时间: 2021-06-08 - * - * @author hxr - * @version 1.0.0 - * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述] - */ -@Component -public class JwtGenerator { - - - @Autowired - private KeyPair keyPair; - - public String createAccessToken(Set authorities, Map additional) { - String payload = new JwtPayloadBuilder() - .exp(12 * 3600) // 默认12小时 - .authorities(authorities) - .additional(additional) - .builder(); - RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); - RsaSigner signer = new RsaSigner(privateKey); - String accessToken = JwtHelper.encode(payload, signer).getEncoded(); - return accessToken; - } - - -} - - - diff --git a/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtPayloadBuilder.java b/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtPayloadBuilder.java deleted file mode 100644 index fbcf186512c5004620a5a7e8e606018b1c80b3f0..0000000000000000000000000000000000000000 --- a/youlai-auth/src/main/java/com/youlai/auth/common/jwt/JwtPayloadBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.youlai.auth.common.jwt; - -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.json.JSONUtil; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * 描述: [类型描述] - * 创建时间: 2021/6/8 - * @author hxr - * @version 1.0.0 - * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述] - */ -public class JwtPayloadBuilder { - - - private Map payload = new HashMap<>(); - - - /** - * jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击 - **/ - private String jti = IdUtil.simpleUUID(); - - /** - * jwt的签发时间 - **/ - private LocalDateTime iat = LocalDateTime.now(); - - /** - * jwt的过期时间,这个过期时间必须要大于签发时间 - **/ - private LocalDateTime exp; - - /** - * 权限集 - */ - private Set authorities = new HashSet<>(); - - /** - * 附加的属性 - */ - private Map additional; - - - public JwtPayloadBuilder exp(int seconds) { - this.exp = this.iat.plusSeconds(seconds); - return this; - } - - public JwtPayloadBuilder authorities(Set authorities) { - this.authorities = authorities; - return this; - } - - public JwtPayloadBuilder additional(Map additional) { - this.additional = additional; - return this; - } - - public String builder() { - payload.put("jti", jti); - payload.put("iat", this.iat.toEpochSecond(ZoneOffset.of("+8"))); - payload.put("exp", this.exp.toEpochSecond(ZoneOffset.of("+8"))); - if (CollectionUtil.isNotEmpty(additional)) { - payload.putAll(additional); - } - payload.put("authorities", this.authorities.toArray()); - return JSONUtil.toJsonStr(payload); - } - - -} diff --git a/youlai-auth/src/main/java/com/youlai/auth/controller/OAuthController.java b/youlai-auth/src/main/java/com/youlai/auth/controller/OAuthController.java index d7013315cbae016345cb7e2e761f3e81aff158a5..afad26f7c3371a5dc2a9d2a11c2a806488674cd4 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/controller/OAuthController.java +++ b/youlai-auth/src/main/java/com/youlai/auth/controller/OAuthController.java @@ -4,10 +4,10 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.RSAKey; -import com.youlai.auth.common.constant.OAuthConstants; -import com.youlai.common.constant.AuthConstants; +import com.youlai.auth.common.constant.AuthConstants; import com.youlai.common.result.Result; import com.youlai.common.web.util.JwtUtils; +import com.youlai.common.web.util.RequestUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -60,7 +60,7 @@ public class OAuthController { * 方式一:client_id、client_secret放在请求路径中(注:当前版本已废弃) * 方式二:放在请求头(Request Headers)中的Authorization字段,且经过加密,例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret */ - String clientId = JwtUtils.getOAuth2ClientId(); + String clientId = RequestUtils.getOAuth2ClientId(); log.info("OAuth认证授权 客户端ID:{},请求参数:{}", clientId, JSONUtil.toJsonStr(parameters)); /** @@ -69,7 +69,7 @@ public class OAuthController { * 请求头自动填充,token必须原生返回,否则显示 undefined undefined * 账号/密码: client_id/client_secret : client/123456 */ - if (OAuthConstants.TEST_CLIENT_ID.equals(clientId)) { + if (AuthConstants.TEST_CLIENT_ID.equals(clientId)) { return tokenEndpoint.postAccessToken(principal, parameters).getBody(); } @@ -81,15 +81,15 @@ public class OAuthController { @DeleteMapping("/logout") public Result logout() { JSONObject payload = JwtUtils.getJwtPayload(); - String jti = payload.getStr(AuthConstants.JWT_JTI); // JWT唯一标识 - Long expireTime = payload.getLong(AuthConstants.JWT_EXP); // JWT过期时间戳(单位:秒) + String jti = payload.getStr(com.youlai.common.constant.AuthConstants.JWT_JTI); // JWT唯一标识 + Long expireTime = payload.getLong(com.youlai.common.constant.AuthConstants.JWT_EXP); // JWT过期时间戳(单位:秒) if (expireTime != null) { long currentTime = System.currentTimeMillis() / 1000;// 当前时间(单位:秒) if (expireTime > currentTime) { // token未过期,添加至缓存作为黑名单限制访问,缓存时间为token过期剩余时间 - redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (expireTime - currentTime), TimeUnit.SECONDS); + redisTemplate.opsForValue().set(com.youlai.common.constant.AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (expireTime - currentTime), TimeUnit.SECONDS); } } else { // token 永不过期则永久加入黑名单 - redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null); + redisTemplate.opsForValue().set(com.youlai.common.constant.AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null); } return Result.success("注销成功"); } diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/config/AuthorizationServerConfig.java b/youlai-auth/src/main/java/com/youlai/auth/security/config/AuthorizationServerConfig.java index b16ad8b453d4f94572367dded6bc718f70566771..63f577ceec9110537871dcf5d6374468c4c43d33 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/config/AuthorizationServerConfig.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/config/AuthorizationServerConfig.java @@ -1,15 +1,17 @@ package com.youlai.auth.security.config; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpStatus; import cn.hutool.json.JSONUtil; +import com.youlai.auth.common.constant.AuthConstants; import com.youlai.auth.security.core.clientdetails.ClientDetailsServiceImpl; import com.youlai.auth.security.core.userdetails.member.MemberUserDetails; import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; -import com.youlai.auth.security.core.userdetails.system.SysUserDetails; -import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl; -import com.youlai.auth.security.extension.wechat.WechatTokenGranter; -import com.youlai.auth.security.core.CustomUserDetailsByNameServiceWrapper; +import com.youlai.auth.security.core.userdetails.user.SysUserDetails; +import com.youlai.auth.security.core.userdetails.user.SysUserDetailsServiceImpl; +import com.youlai.auth.security.extension.memeber.wechat.WechatTokenGranter; +import com.youlai.auth.security.extension.PreAuthenticatedUserDetailsService; import com.youlai.common.result.Result; import com.youlai.common.result.ResultCode; import lombok.RequiredArgsConstructor; @@ -53,8 +55,6 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap private final SysUserDetailsServiceImpl sysUserDetailsService; private final MemberUserDetailsServiceImpl memberUserDetailsService; - private final List userDetailsServices; - /** * OAuth2客户端 */ @@ -87,10 +87,11 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap .accessTokenConverter(jwtAccessTokenConverter()) .tokenEnhancer(tokenEnhancerChain) .tokenGranter(compositeTokenGranter) - // .userDetailsService(sysUserDetailsService) - // refresh token有两种使用方式:重复使用(true)、非重复使用(false),默认为true - // 1 重复使用:access token过期刷新时, refresh token过期时间未改变,仍以初次生成的时间为准 - // 2 非重复使用:access token过期刷新时, refresh token过期时间延续,在refresh token有效期内刷新便永不失效达到无需再次登录的目的 + //.userDetailsService(sysUserDetailsService) + /** refresh token有两种使用方式:重复使用(true)、非重复使用(false),默认为true + * 1 重复使用:access token过期刷新时, refresh token过期时间未改变,仍以初次生成的时间为准 + * 2 非重复使用:access token过期刷新时, refresh token过期时间延续,在refresh token有效期内刷新便永不失效达到无需再次登录的目的 + */ .reuseRefreshTokens(true) .tokenServices(tokenServices(endpoints)); // 自定义的TokenService } @@ -111,13 +112,21 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap tokenServices.setTokenEnhancer(tokenEnhancerChain); PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); - provider.setPreAuthenticatedUserDetailsService(new CustomUserDetailsByNameServiceWrapper<>(userDetailsServices)); + provider.setPreAuthenticatedUserDetailsService(new PreAuthenticatedUserDetailsService<>(refreshTokenUserDetailsServiceMap())); tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider))); return tokenServices; } + public Map refreshTokenUserDetailsServiceMap() { + Map clientUserDetailsServiceMap = new HashMap<>(); + clientUserDetailsServiceMap.put(AuthConstants.ADMIN_CLIENT_ID, sysUserDetailsService); + clientUserDetailsServiceMap.put(AuthConstants.APP_CLIENT_ID, memberUserDetailsService); + return clientUserDetailsServiceMap; + } + + /** * 使用非对称加密算法对token签名 */ @@ -150,10 +159,16 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap SysUserDetails sysUserDetails = (SysUserDetails) principal; additionalInfo.put("userId", sysUserDetails.getUserId()); additionalInfo.put("username", sysUserDetails.getUsername()); + if (StrUtil.isNotBlank(sysUserDetails.getAuthenticationMethod())) { + additionalInfo.put("authenticationMethod", sysUserDetails.getAuthenticationMethod()); + } } else if (principal instanceof MemberUserDetails) { MemberUserDetails memberUserDetails = (MemberUserDetails) principal; additionalInfo.put("userId", memberUserDetails.getUserId()); additionalInfo.put("username", memberUserDetails.getUsername()); + if (StrUtil.isNotBlank(memberUserDetails.getAuthenticationMethod())) { + additionalInfo.put("authenticationMethod", memberUserDetails.getAuthenticationMethod()); + } } ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/config/WebSecurityConfig.java b/youlai-auth/src/main/java/com/youlai/auth/security/config/WebSecurityConfig.java index 33f38da60b95f12ccf1fae5439740a38d7bcc781..fdbdfab17e51aa20e4e759e378070c4173bfe9b0 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/config/WebSecurityConfig.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/config/WebSecurityConfig.java @@ -1,9 +1,7 @@ package com.youlai.auth.security.config; import cn.binarywang.wx.miniapp.api.WxMaService; -import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; -import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl; -import com.youlai.auth.security.extension.wechat.WechatAuthenticationProvider; +import com.youlai.auth.security.extension.memeber.wechat.WechatAuthenticationProvider; import com.youlai.mall.ums.api.MemberFeignClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/core/CustomUserDetailsByNameServiceWrapper.java b/youlai-auth/src/main/java/com/youlai/auth/security/core/CustomUserDetailsByNameServiceWrapper.java deleted file mode 100644 index 96c3285f8012e1ef61d118e6cb6eafbe32ec716c..0000000000000000000000000000000000000000 --- a/youlai-auth/src/main/java/com/youlai/auth/security/core/CustomUserDetailsByNameServiceWrapper.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.youlai.auth.security.core; - -import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; -import com.youlai.auth.security.core.userdetails.system.SysUserDetailsServiceImpl; -import com.youlai.auth.security.extension.wechat.WechatAuthenticationToken; -import com.youlai.common.web.util.JwtUtils; -import lombok.NoArgsConstructor; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; -import org.springframework.util.Assert; - -import java.util.List; - -/** - * @author xianrui - * @date 2021/10/2 - */ -@NoArgsConstructor -public class CustomUserDetailsByNameServiceWrapper implements AuthenticationUserDetailsService, InitializingBean { - - - private List userDetailsServiceList; - - public CustomUserDetailsByNameServiceWrapper(List userDetailsServiceList) { - Assert.notNull(userDetailsServiceList, "userDetailsService cannot be null."); - this.userDetailsServiceList = userDetailsServiceList; - } - - @Override - public void afterPropertiesSet() throws Exception { - Assert.notNull(this.userDetailsServiceList, "UserDetailsService must be set"); - } - - @Override - public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException { - UserDetailsService userDetailsService = null; - - - String clientId = JwtUtils.getOAuth2ClientId(); - - for (UserDetailsService detailsService : userDetailsServiceList) { - if (clientId.equals("youlai-mall-weapp")) { - if (detailsService instanceof MemberUserDetailsServiceImpl) { - userDetailsService = detailsService; - continue; - } - }else{ - if (detailsService instanceof SysUserDetailsServiceImpl) { - userDetailsService = detailsService; - continue; - } - } - } - return userDetailsService.loadUserByUsername(authentication.getName()); - } -} diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetails.java b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetails.java index e968469163c30417f1b92412c487d997e1376e8a..6ae08e83c7d37dba6d38a517c07741633c226bb4 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetails.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetails.java @@ -21,9 +21,14 @@ import java.util.HashSet; public class MemberUserDetails implements UserDetails { private Long userId; - private String openId; + private String username; private Boolean enabled; + /** + * 认证方式 + */ + private String authenticationMethod; + /** * 小程序会员用户体系 @@ -32,7 +37,7 @@ public class MemberUserDetails implements UserDetails { */ public MemberUserDetails(MemberAuthDTO member) { this.setUserId(member.getUserId()); - this.setOpenId(member.getOpenId()); + this.setUsername(member.getUsername()); this.setEnabled(GlobalConstants.STATUS_YES.equals(member.getStatus())); } @@ -49,7 +54,7 @@ public class MemberUserDetails implements UserDetails { @Override public String getUsername() { - return this.openId; + return this.username; } @Override diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetailsServiceImpl.java b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetailsServiceImpl.java index cb9c056a4169942e34aabdba8042cc42389a3599..a73bda7cc9fef7ada5fb7ab0eb2cc6f657344954 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetailsServiceImpl.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/member/MemberUserDetailsServiceImpl.java @@ -1,11 +1,11 @@ package com.youlai.auth.security.core.userdetails.member; +import com.youlai.common.enums.AuthenticationMethodEnum; import com.youlai.common.result.Result; import com.youlai.common.result.ResultCode; import com.youlai.mall.ums.api.MemberFeignClient; import com.youlai.mall.ums.pojo.dto.MemberAuthDTO; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.AccountExpiredException; import org.springframework.security.authentication.DisabledException; @@ -16,7 +16,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** - * 系统用户体系业务类 + * 系统管理用户 * * @author xianrui */ @@ -27,15 +27,20 @@ public class MemberUserDetailsServiceImpl implements UserDetailsService { private final MemberFeignClient memberFeignClient; - @SneakyThrows @Override - public UserDetails loadUserByUsername(String openid) throws UsernameNotFoundException { + public UserDetails loadUserByUsername(String username) { + return null; + } + + + public UserDetails loadUserByMobile(String mobile) { MemberUserDetails userDetails = null; - Result result = memberFeignClient.loadUserByOpenId(openid); + Result result = memberFeignClient.loadUserByMobile(mobile); if (Result.isSuccess(result)) { MemberAuthDTO member = result.getData(); if (null != member) { userDetails = new MemberUserDetails(member); + userDetails.setAuthenticationMethod(AuthenticationMethodEnum.MOBILE.getValue()); // 认证方式:OpenId } } if (userDetails == null) { @@ -50,4 +55,26 @@ public class MemberUserDetailsServiceImpl implements UserDetailsService { return userDetails; } + + public UserDetails loadUserByOpenId(String openId) { + MemberUserDetails userDetails = null; + Result result = memberFeignClient.loadUserByOpenId(openId); + if (Result.isSuccess(result)) { + MemberAuthDTO member = result.getData(); + if (null != member) { + userDetails = new MemberUserDetails(member); + userDetails.setAuthenticationMethod(AuthenticationMethodEnum.OPENID.getValue()); // 认证方式:OpenId + } + } + if (userDetails == null) { + throw new UsernameNotFoundException(ResultCode.USER_NOT_EXIST.getMsg()); + } else if (!userDetails.isEnabled()) { + throw new DisabledException("该账户已被禁用!"); + } else if (!userDetails.isAccountNonLocked()) { + throw new LockedException("该账号已被锁定!"); + } else if (!userDetails.isAccountNonExpired()) { + throw new AccountExpiredException("该账号已过期!"); + } + return userDetails; + } } diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetails.java b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetails.java similarity index 92% rename from youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetails.java rename to youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetails.java index 7a21d7a9a68b4681de96341a56d7f74f3a45afa6..50652ebfc8fe830fee507cc2cae7bf8ce2b01ac4 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetails.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetails.java @@ -1,4 +1,4 @@ -package com.youlai.auth.security.core.userdetails.system; +package com.youlai.auth.security.core.userdetails.user; import cn.hutool.core.collection.CollectionUtil; import com.youlai.admin.pojo.dto.UserAuthDTO; @@ -14,7 +14,7 @@ import java.util.Collection; /** - * 用户认证信息 + * 系统管理用户认证信息 * * @author haoxianrui * @date 2021/9/27 @@ -26,6 +26,7 @@ public class SysUserDetails implements UserDetails { * 扩展字段 */ private Long userId; + private String authenticationMethod; /** * 默认字段 @@ -36,9 +37,7 @@ public class SysUserDetails implements UserDetails { private Collection authorities; /** - * 系统管理用户体系 - * - * @param user 系统管理用户认证信息 + * 系统管理用户 */ public SysUserDetails(UserAuthDTO user) { this.setUserId(user.getUserId()); diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetailsServiceImpl.java b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetailsServiceImpl.java similarity index 97% rename from youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetailsServiceImpl.java rename to youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetailsServiceImpl.java index e16742ac641314d0bb9c4bda7e9a7785478d8218..161b89d7cf5406fd0fa3a152dc046787cb452a38 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/system/SysUserDetailsServiceImpl.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/core/userdetails/user/SysUserDetailsServiceImpl.java @@ -1,4 +1,4 @@ -package com.youlai.auth.security.core.userdetails.system; +package com.youlai.auth.security.core.userdetails.user; import com.youlai.admin.api.UserFeignClient; import com.youlai.admin.pojo.dto.UserAuthDTO; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/extension/PreAuthenticatedUserDetailsService.java b/youlai-auth/src/main/java/com/youlai/auth/security/extension/PreAuthenticatedUserDetailsService.java new file mode 100644 index 0000000000000000000000000000000000000000..a7609a41fced65992ead21e39f951e20e7c27cfe --- /dev/null +++ b/youlai-auth/src/main/java/com/youlai/auth/security/extension/PreAuthenticatedUserDetailsService.java @@ -0,0 +1,61 @@ +package com.youlai.auth.security.extension; + +import com.youlai.auth.common.constant.AuthConstants; +import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; +import com.youlai.common.enums.AuthenticationMethodEnum; +import com.youlai.common.web.util.RequestUtils; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.util.Assert; + +import java.util.Map; + +/** + * UserDetailsService 工厂类 + * + * @author xianrui + * @date 2021/10/2 + */ +@NoArgsConstructor +public class PreAuthenticatedUserDetailsService implements AuthenticationUserDetailsService, InitializingBean { + + private Map userDetailsServiceMap; + + public PreAuthenticatedUserDetailsService(Map userDetailsServiceMap) { + Assert.notNull(userDetailsServiceMap, "userDetailsService cannot be null."); + this.userDetailsServiceMap = userDetailsServiceMap; + } + + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.userDetailsServiceMap, "UserDetailsService must be set"); + } + + @Override + public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException { + String clientId = RequestUtils.getOAuth2ClientId(); + AuthenticationMethodEnum authenticationMethodEnum = AuthenticationMethodEnum.getByValue(RequestUtils.getAuthenticationMethod()); + UserDetailsService userDetailsService = userDetailsServiceMap.get(clientId); + if (clientId.equals(AuthConstants.APP_CLIENT_ID)) { + MemberUserDetailsServiceImpl memberUserDetailsService = (MemberUserDetailsServiceImpl) userDetailsService; + switch (authenticationMethodEnum) { + case OPENID: + return memberUserDetailsService.loadUserByOpenId(authentication.getName()); + default: + return memberUserDetailsService.loadUserByUsername(authentication.getName()); + } + } else if (clientId.equals(AuthConstants.ADMIN_CLIENT_ID)) { + switch (authenticationMethodEnum) { + default: + return userDetailsService.loadUserByUsername(authentication.getName()); + } + } else { + return userDetailsService.loadUserByUsername(authentication.getName()); + } + } +} diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationProvider.java b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationProvider.java similarity index 91% rename from youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationProvider.java rename to youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationProvider.java index 2e5c64f3c75d2e75f5d767a3b0837db775d34798..547d03917bd73ade3c5119604579a42ea1555604 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationProvider.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationProvider.java @@ -1,15 +1,15 @@ -package com.youlai.auth.security.extension.wechat; +package com.youlai.auth.security.extension.memeber.wechat; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.hutool.core.bean.BeanUtil; +import com.youlai.auth.security.core.userdetails.member.MemberUserDetailsServiceImpl; import com.youlai.common.result.Result; import com.youlai.common.result.ResultCode; import com.youlai.mall.ums.api.MemberFeignClient; import com.youlai.mall.ums.pojo.dto.MemberAuthDTO; import com.youlai.mall.ums.pojo.entity.UmsMember; import lombok.Data; -import lombok.SneakyThrows; import me.chanjar.weixin.common.error.WxErrorException; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; @@ -27,12 +27,9 @@ import java.util.HashSet; public class WechatAuthenticationProvider implements AuthenticationProvider { private UserDetailsService userDetailsService; - private WxMaService wxMaService; - private MemberFeignClient memberFeignClient; - /** * 认证验证 * @@ -62,7 +59,7 @@ public class WechatAuthenticationProvider implements AuthenticationProvider { memberFeignClient.add(member); } - UserDetails userDetails = userDetailsService.loadUserByUsername(openid); + UserDetails userDetails = ((MemberUserDetailsServiceImpl)userDetailsService).loadUserByOpenId(openid); WechatAuthenticationToken result = new WechatAuthenticationToken(userDetails, new HashSet<>()); result.setDetails(authentication.getDetails()); return result; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationToken.java b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationToken.java similarity index 96% rename from youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationToken.java rename to youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationToken.java index 485370b2eeba1c7f79c10c71da3dfcf9a9692473..6f0e00741adc4c6ae376adff0addf953bbcf6fba 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatAuthenticationToken.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatAuthenticationToken.java @@ -1,4 +1,4 @@ -package com.youlai.auth.security.extension.wechat; +package com.youlai.auth.security.extension.memeber.wechat; import lombok.Getter; import org.springframework.security.authentication.AbstractAuthenticationToken; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatTokenGranter.java b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatTokenGranter.java similarity index 97% rename from youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatTokenGranter.java rename to youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatTokenGranter.java index d11173c78badf28789d51dc399db51013638458f..bf26c3e4c4e0462a1a61539ee79e414a49b02ff5 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatTokenGranter.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatTokenGranter.java @@ -1,4 +1,4 @@ -package com.youlai.auth.security.extension.wechat; +package com.youlai.auth.security.extension.memeber.wechat; import cn.hutool.json.JSONUtil; import org.springframework.security.authentication.*; diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatUserInfo.java b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatUserInfo.java similarity index 77% rename from youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatUserInfo.java rename to youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatUserInfo.java index 9727ad78500d345032b8accee7914c8d374645b3..3d1b256b8fb74c298f013cfaeb1e13b321868938 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/extension/wechat/WechatUserInfo.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/extension/memeber/wechat/WechatUserInfo.java @@ -1,16 +1,13 @@ -package com.youlai.auth.security.extension.wechat; +package com.youlai.auth.security.extension.memeber.wechat; import lombok.Data; /** - * 微信用户信息 - * * @author xianrui - * @date 2021/9/29 + * @date 2021/10/4 */ @Data public class WechatUserInfo { - private String avatarUrl; private String city; diff --git a/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java b/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java index 2c58fd5275e131f82238f17e01dd46884207b9d7..9982079b268304d7d1bfaae4363a28a6e732e82e 100644 --- a/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java +++ b/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java @@ -67,5 +67,10 @@ public interface AuthConstants { */ String SAVE_MENU_PATH = "/youlai-admin/api/v1/menus"; + /** + * 认证方式 + */ + String AUTHENTICATION_METHOD = "authenticationMethod"; + } diff --git a/youlai-common/common-core/src/main/java/com/youlai/common/enums/AuthenticationMethodEnum.java b/youlai-common/common-core/src/main/java/com/youlai/common/enums/AuthenticationMethodEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..3aac09f7624d109132145717d1b5c8e0f0d57704 --- /dev/null +++ b/youlai-common/common-core/src/main/java/com/youlai/common/enums/AuthenticationMethodEnum.java @@ -0,0 +1,39 @@ +package com.youlai.common.enums; + +import lombok.Getter; + +/** + * 认证方式枚举 + * + * @author xianrui + * @date 2021/10/4 + */ +public enum AuthenticationMethodEnum { + + USERNAME("username", "用户名"), + MOBILE("mobile", "手机号"), + OPENID("openId", "开放式认证系统唯一身份标识"); + + @Getter + private String value; + + @Getter + private String label; + + AuthenticationMethodEnum(String value, String label) { + this.value = value; + this.label = label; + } + + public static AuthenticationMethodEnum getByValue(String value) { + AuthenticationMethodEnum authenticationMethodEnum = null; + for (AuthenticationMethodEnum item : values()) { + if (item.getValue().equals(value)) { + authenticationMethodEnum = item; + continue; + } + } + return authenticationMethodEnum; + } + +} diff --git a/youlai-common/common-core/src/main/java/com/youlai/common/enums/BusinessTypeEnum.java b/youlai-common/common-core/src/main/java/com/youlai/common/enums/BusinessTypeEnum.java index 089aec3531ea5920ca28c95b2fbf65a97f5d0ccc..23c1f115eea4cdb1fd5c384a9de892b9d691c25d 100644 --- a/youlai-common/common-core/src/main/java/com/youlai/common/enums/BusinessTypeEnum.java +++ b/youlai-common/common-core/src/main/java/com/youlai/common/enums/BusinessTypeEnum.java @@ -27,11 +27,13 @@ public enum BusinessTypeEnum { } public static BusinessTypeEnum getValue(String code) { + BusinessTypeEnum businessTypeEnum=null; for (BusinessTypeEnum value : values()) { if (value.getCode().equals(code)) { - return value; + businessTypeEnum =value; + continue; } } - return null; + return businessTypeEnum; } } diff --git a/youlai-common/common-web/pom.xml b/youlai-common/common-web/pom.xml index 413e11330dc09d3f95c58a2b10d4bd1db5d74a18..988704ba66fb2d447eadacd3de1bed934a4f22d2 100644 --- a/youlai-common/common-web/pom.xml +++ b/youlai-common/common-web/pom.xml @@ -58,6 +58,11 @@ ip2region + + com.nimbusds + nimbus-jose-jwt + + diff --git a/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java index 00a17ac5b50f7410d845fe9d48b6a2f64035f6a3..812649d5137e2bc7b5ef1a6040373db97eb2a7d6 100644 --- a/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java +++ b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java @@ -55,36 +55,7 @@ public class JwtUtils { return username; } - /** - * 获取登录认证的客户端ID - * - * 兼容两种方式获取OAuth2客户端信息(client_id、client_secret) - * 方式一:client_id、client_secret放在请求路径中 - * 方式二:放在请求头(Request Headers)中的Authorization字段,且经过加密,例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret - * - * @return - */ - @SneakyThrows - public static String getOAuth2ClientId() { - String clientId; - HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - - // 从请求路径中获取 - clientId = request.getParameter(AuthConstants.CLIENT_ID_KEY); - if (StrUtil.isNotBlank(clientId)) { - return clientId; - } - - // 从请求头获取 - String basic = request.getHeader(AuthConstants.AUTHORIZATION_KEY); - if (StrUtil.isNotBlank(basic) && basic.startsWith(AuthConstants.BASIC_PREFIX)) { - basic = basic.replace(AuthConstants.BASIC_PREFIX, Strings.EMPTY); - String basicPlainText = new String(Base64.getDecoder().decode(basic.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); - clientId = basicPlainText.split(":")[0]; //client:secret - } - return clientId; - } /** * JWT获取用户角色列表 diff --git a/youlai-common/common-web/src/main/java/com/youlai/common/web/util/RequestUtils.java b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/RequestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..85f3a47b13140a8172793fc4c71df242cbbce28c --- /dev/null +++ b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/RequestUtils.java @@ -0,0 +1,83 @@ +package com.youlai.common.web.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.nimbusds.jose.JWSObject; +import com.youlai.common.constant.AuthConstants; +import com.youlai.common.enums.AuthenticationMethodEnum; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * 请求工具类 + * + * @author xianrui + */ +@Slf4j +public class RequestUtils { + @SneakyThrows + public static String getGrantType() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String grantType = request.getParameter(AuthConstants.GRANT_TYPE_KEY); + return grantType; + } + + + /** + * 获取登录认证的客户端ID + *

+ * 兼容两种方式获取OAuth2客户端信息(client_id、client_secret) + * 方式一:client_id、client_secret放在请求路径中 + * 方式二:放在请求头(Request Headers)中的Authorization字段,且经过加密,例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret + * + * @return + */ + @SneakyThrows + public static String getOAuth2ClientId() { + + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + + // 从请求路径中获取 + String clientId = request.getParameter(AuthConstants.CLIENT_ID_KEY); + if (StrUtil.isNotBlank(clientId)) { + return clientId; + } + + // 从请求头获取 + String basic = request.getHeader(AuthConstants.AUTHORIZATION_KEY); + if (StrUtil.isNotBlank(basic) && basic.startsWith(AuthConstants.BASIC_PREFIX)) { + basic = basic.replace(AuthConstants.BASIC_PREFIX, Strings.EMPTY); + String basicPlainText = new String(Base64.getDecoder().decode(basic.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + clientId = basicPlainText.split(":")[0]; //client:secret + } + return clientId; + } + + /** + * 解析JWT获取获取认证方式 + * + * @return + */ + @SneakyThrows + public static String getAuthenticationMethod() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String refreshToken = request.getParameter(AuthConstants.REFRESH_TOKEN); + + String payload = StrUtil.toString(JWSObject.parse(refreshToken).getPayload()); + JSONObject jsonObject = JSONUtil.parseObj(payload); + + String authenticationMethod = jsonObject.getStr(AuthConstants.AUTHENTICATION_METHOD); + if (StrUtil.isBlank(authenticationMethod)) { + authenticationMethod = AuthenticationMethodEnum.USERNAME.getValue(); + } + return authenticationMethod; + } +} diff --git a/youlai-gateway/pom.xml b/youlai-gateway/pom.xml index 428460f5cb948ba90386ea61c983e9cf7794b8f4..fcb2e0781a77be1aa917cddd011ee36e99241771 100644 --- a/youlai-gateway/pom.xml +++ b/youlai-gateway/pom.xml @@ -102,49 +102,21 @@ common-redis + + + com.github.penggle + kaptcha + + ${project.artifactId} - org.springframework.boot spring-boot-maven-plugin - - com.spotify - docker-maven-plugin - 1.2.2 - - - - - build-image - package - - build - - - - - ${project.artifactId} - - - latest - - - ${project.basedir}/src/main/docker - true - - - / - ${project.build.directory} - ${project.build.finalName}.jar - - - - diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/config/CaptchaConfig.java b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/config/CaptchaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..e203d3533bcdd9c0b0a5fcff811ff73bf3dc4292 --- /dev/null +++ b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/config/CaptchaConfig.java @@ -0,0 +1,57 @@ +package com.youlai.gateway.kaptcha.config; + +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +/** + * @author xianrui + * @date 2021/10/4 + */ +@Configuration +public class CaptchaConfig { + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty("kaptcha.border", "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty("kaptcha.border.color", "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty("kaptcha.textproducer.font.color", "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty("kaptcha.image.width", "160"); + // 验证码图片高度 默认为50 + properties.setProperty("kaptcha.image.height", "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty("kaptcha.textproducer.font.size", "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty("kaptcha.session.key", "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty("kaptcha.textproducer.impl", "com.youlai.gateway.kaptcha.handler.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty("kaptcha.textproducer.char.space", "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty("kaptcha.textproducer.char.length", "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, + // fontSize) + properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty("kaptcha.noise.color", "white"); + // 干扰实现类 + properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple + // 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy + // 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + +} diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/CaptchaImageHandler.java b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/CaptchaImageHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..fd37d34ceb47ae384781ba472ebc69da3d1fbee1 --- /dev/null +++ b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/CaptchaImageHandler.java @@ -0,0 +1,63 @@ +package com.youlai.gateway.kaptcha.handler; + +import cn.hutool.core.io.FastByteArrayOutputStream; +import cn.hutool.core.util.IdUtil; +import com.google.code.kaptcha.Producer; +import lombok.RequiredArgsConstructor; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpStatus; +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 javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author xianrui + * @date 2021/10/4 + */ +@Component +@RequiredArgsConstructor +public class CaptchaImageHandler implements HandlerFunction { + + //随机数code_key + public static final String DEFAULT_CODE_KEY = "random_code_"; + + private final Producer producer; + + private final RedisTemplate redisTemplate; + + @Override + public Mono handle(ServerRequest serverRequest) { + // 生成验证码 + String capText = producer.createText(); + String capStr = capText.substring(0, capText.lastIndexOf("@")); + String code = capText.substring(capText.lastIndexOf("@") + 1); + BufferedImage image = producer.createImage(capStr); + // 保存验证码信息 + String randomStr = IdUtil.simpleUUID(); + redisTemplate.opsForValue().set(DEFAULT_CODE_KEY + randomStr, code, 60, TimeUnit.SECONDS); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try { + ImageIO.write(image, "jpg", os); + } catch (IOException e) { + return Mono.error(e); + } + return ServerResponse.status(HttpStatus.OK) + .contentType(MediaType.IMAGE_JPEG) + .header("randomstr", randomStr) + .body(BodyInserters.fromResource(new ByteArrayResource(os.toByteArray()))); + } + + + +} diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/KaptchaTextCreator.java b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/KaptchaTextCreator.java new file mode 100644 index 0000000000000000000000000000000000000000..fce54668b7b522f0a9ef6fe12dffaa8e0cdd64a5 --- /dev/null +++ b/youlai-gateway/src/main/java/com/youlai/gateway/kaptcha/handler/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.youlai.gateway.kaptcha.handler; + +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * 验证码文本生成器 + * + * @author xianrui + * @date 2021/10/4 + */ +public class KaptchaTextCreator extends DefaultTextCreator { + + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + private SecureRandom rand = SecureRandom.getInstanceStrong(); + + public KaptchaTextCreator() throws NoSuchAlgorithmException { + } + + @Override + public String getText() { + Integer result = 0; + int x = this.rand.nextInt(10); + int y = this.rand.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = (int) Math.round(rand.nextDouble() * 2); + if (randomoperands == 0) { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } else if (randomoperands == 1) { + if (!(x == 0) && y % x == 0) { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } else { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } else if (randomoperands == 2) { + if (x >= y) { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } else { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } else { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } + +} diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/router/CaptchaImageRouter.java b/youlai-gateway/src/main/java/com/youlai/gateway/router/CaptchaImageRouter.java new file mode 100644 index 0000000000000000000000000000000000000000..88b2a434cb660348e99847b18c156b20567364bd --- /dev/null +++ b/youlai-gateway/src/main/java/com/youlai/gateway/router/CaptchaImageRouter.java @@ -0,0 +1,28 @@ +package com.youlai.gateway.router; + +import com.youlai.gateway.kaptcha.handler.CaptchaImageHandler; +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; + +/** + * 图片验证码路由 + * + * @author xianrui + * @date 2021/10/4 + */ +@Configuration +public class CaptchaImageRouter { + + @Bean + public RouterFunction routeFunction(CaptchaImageHandler captchaImageHandler) { + return RouterFunctions + .route(RequestPredicates.GET("/code") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), captchaImageHandler::handle); + } + +} diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java b/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java index ba37b378e95d21e6ca870d92f2a3b48a4078f833..1d51431907c280a6e42062f00669cc0a616a845b 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java @@ -68,8 +68,7 @@ public class SecurityGlobalFilter implements GlobalFilter, Ordered { // 解析JWT获取jti,以jti为key判断redis的黑名单列表是否存在,存在则拦截访问 token = StrUtil.replaceIgnoreCase(token,AuthConstants.JWT_PREFIX, Strings.EMPTY); - JWSObject jwsObject = JWSObject.parse(token); - String payload = jwsObject.getPayload().toString(); + String payload = StrUtil.toString(JWSObject.parse(token).getPayload()); JSONObject jsonObject = JSONUtil.parseObj(payload); String jti = jsonObject.getStr(AuthConstants.JWT_JTI); Boolean isBlack = redisTemplate.hasKey(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti);