Przeglądaj źródła

第三方免密登录

home 2 lat temu
rodzic
commit
f3537650a1
16 zmienionych plików z 612 dodań i 218 usunięć
  1. 50 0
      bladex/blade-auth/src/main/java/org/springblade/auth/enums/LoginTypeEnum.java
  2. 45 28
      bladex/blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java
  3. 166 158
      bladex/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java
  4. 26 0
      bladex/blade-auth/src/main/java/org/springblade/auth/service/MyAuthenticationProvider.java
  5. 79 0
      bladex/blade-auth/src/main/java/org/springblade/auth/utils/JwtUtil.java
  6. 1 2
      bladex/blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/entity/User.java
  7. 1 2
      bladex/blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java
  8. 9 3
      hx-service/blade-ex/src/main/java/com/fjhx/controller/TenantController.java
  9. 2 1
      hx-service/blade-ex/src/main/java/com/fjhx/service/ITenantService.java
  10. 8 7
      hx-service/blade-ex/src/main/java/com/fjhx/service/impl/TenantServiceImpl.java
  11. 1 17
      hx-service/blade-ex/src/main/java/com/fjhx/utils/JwtUtil.java
  12. 11 0
      hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/HxClientConstant.java
  13. 55 0
      hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/HxClientUtil.java
  14. 79 0
      hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/JwtUtil.java
  15. 51 0
      hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/entity/CreateTenantVo.java
  16. 28 0
      hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/entity/ResultEntity.java

+ 50 - 0
bladex/blade-auth/src/main/java/org/springblade/auth/enums/LoginTypeEnum.java

@@ -0,0 +1,50 @@
+package org.springblade.auth.enums;
+
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 入库类型
+ */
+@Getter
+public enum LoginTypeEnum {
+
+    STORAGE_APPLET("storageApplet"),
+    FOREIGN_TRADE_SUPERMAN("foreignTradeSuperman"),
+    LOGIN_WITHOUT_VERIFICATION_CODE("loginWithoutVerificationCode"),
+    DEFAULT_LOGIN("defaultLogin"),
+    ;
+
+    private final String type;
+
+    private static final HashMap<String, LoginTypeEnum> map = new HashMap<>();
+
+    LoginTypeEnum(String type) {
+        this.type = type;
+    }
+
+    static {
+        for (LoginTypeEnum value : LoginTypeEnum.values()) {
+            map.put(value.getType(), value);
+        }
+    }
+
+    /**
+     * 根据type获取枚举
+     */
+    public static LoginTypeEnum get(String type) {
+        LoginTypeEnum loginTypeEnum = map.get(type);
+        return loginTypeEnum == null ? DEFAULT_LOGIN : loginTypeEnum;
+    }
+
+    /**
+     * 获取无需密码登录类型列表
+     */
+    public static List<LoginTypeEnum> getNoPasswordRequiredTypeList() {
+        return Arrays.asList(STORAGE_APPLET, FOREIGN_TRADE_SUPERMAN);
+    }
+
+}

+ 45 - 28
bladex/blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java

@@ -2,7 +2,9 @@ package org.springblade.auth.granter;
 
 import lombok.extern.slf4j.Slf4j;
 import org.springblade.auth.constant.WxAppletConstant;
+import org.springblade.auth.enums.LoginTypeEnum;
 import org.springblade.auth.service.BladeUserDetails;
+import org.springblade.auth.utils.JwtUtil;
 import org.springblade.auth.utils.TokenUtil;
 import org.springblade.auth.utils.WxAppletUtil;
 import org.springblade.common.cache.CacheNames;
@@ -13,7 +15,9 @@ import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.core.tool.utils.WebUtil;
 import org.springblade.system.user.entity.User;
 import org.springblade.system.user.feign.IUserClient;
-import org.springframework.security.authentication.*;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
 import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
@@ -61,16 +65,21 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
 
         Authentication userAuth;
 
-        switch (parameters.get("type")) {
+        switch (LoginTypeEnum.get(parameters.get("type"))) {
             // 杰生小程序登录逻辑
-            case "storageApplet":
-                userAuth = storageAppletLogin(parameters, request, WxAppletConstant.STORAGE_APPID, WxAppletConstant.STORAGE_SECRET);
+            case STORAGE_APPLET:
+                userAuth = storageAppletLogin(parameters, WxAppletConstant.STORAGE_APPID, WxAppletConstant.STORAGE_SECRET);
+                break;
+
+            // 外贸超人
+            case FOREIGN_TRADE_SUPERMAN:
+                userAuth = foreignTradeSupermanLogin(parameters);
                 break;
 
             // 无需验证码登录
-            case "loginWithoutVerificationCode":
+            case LOGIN_WITHOUT_VERIFICATION_CODE:
                 // 校验账号密码
-                userAuth = verificationAccountPassword(parameters, request);
+                userAuth = verificationLogin(parameters);
                 break;
 
             // 默认登录
@@ -78,7 +87,7 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
                 // 校验验证码
                 verificationCode(request);
                 // 校验账号密码
-                userAuth = verificationAccountPassword(parameters, request);
+                userAuth = verificationLogin(parameters);
         }
 
         OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
@@ -108,31 +117,20 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
     }
 
     /**
-     * 校验账号密码
+     * 登录校验
      */
-    private Authentication verificationAccountPassword(Map<String, String> parameters, HttpServletRequest request) {
-
+    private Authentication verificationLogin(Map<String, String> parameters) {
         String username = parameters.get("username");
-        String password = parameters.get("password");
-        // Protect from downstream leaks of password
-        parameters.remove("password");
-
-        // 把密码放入attribute中
-        request.setAttribute(TokenUtil.PASSWORD_KEY, password);
+        String password = parameters.remove("password");
 
         Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
         ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
         try {
             userAuth = authenticationManager.authenticate(userAuth);
-        } catch (AccountStatusException | BadCredentialsException ase) {
+        } catch (Exception ase) {
             //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
             throw new InvalidGrantException(ase.getMessage());
         }
-        // If the username/password are wrong the spec says we should send 400/invalid grant
-
-        if (userAuth == null || !userAuth.isAuthenticated()) {
-            throw new InvalidGrantException("Could not authenticate user: " + username);
-        }
 
         return userAuth;
     }
@@ -140,7 +138,7 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
     /**
      * 小程序登录
      */
-    private Authentication storageAppletLogin(Map<String, String> parameters, HttpServletRequest request, String appid, String secret) {
+    private Authentication storageAppletLogin(Map<String, String> parameters, String appid, String secret) {
         Authentication userAuth;
 
         String username = parameters.get("username");
@@ -159,14 +157,13 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
         IUserClient userClient = SpringUtil.getBean(IUserClient.class);
 
         if (ObjectUtil.isNotEmpty(username) && ObjectUtil.isNotEmpty(password)) {
-            userAuth = verificationAccountPassword(parameters, request);
+            userAuth = verificationLogin(parameters);
 
             BladeUserDetails bladeUserDetails = (BladeUserDetails) userAuth.getPrincipal();
             Long userId = bladeUserDetails.getUserId();
 
             User user = new User();
             user.setId(userId);
-            user.setAppletKey(password);
             user.setAppletOpenId(openId);
 
             userClient.updateUserById(user);
@@ -174,15 +171,35 @@ public class CaptchaTokenGranter extends AbstractTokenGranter {
             try {
                 User user = userClient.getUserInfoByAppletOpenId(openId);
                 parameters.put("username", user.getAccount());
-                parameters.put("password", user.getAppletKey());
 
-                userAuth = verificationAccountPassword(parameters, request);
+                userAuth = verificationLogin(parameters);
             } catch (Exception e) {
-                throw new UserDeniedAuthorizationException("无法自动登录");
+                throw new UserDeniedAuthorizationException("登录信息已过期,请重新登录");
             }
         }
 
         return userAuth;
     }
 
+    /**
+     * 外贸超人登录
+     */
+    private Authentication foreignTradeSupermanLogin(Map<String, String> parameters) {
+        String token = parameters.get("token");
+        String username = parameters.get("username");
+
+        if (token == null || username == null) {
+            throw new UserDeniedAuthorizationException("非法登录");
+        }
+
+        String jwtUsername = JwtUtil.parseToken(token, String.class);
+
+        if (!username.equals(jwtUsername)) {
+            throw new UserDeniedAuthorizationException("非法登录");
+        }
+
+        return verificationLogin(parameters);
+    }
+
+
 }

+ 166 - 158
bladex/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java

@@ -19,15 +19,18 @@ package org.springblade.auth.service;
 import com.alibaba.nacos.common.utils.StringUtils;
 import io.jsonwebtoken.Claims;
 import lombok.AllArgsConstructor;
-import lombok.SneakyThrows;
 import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.enums.LoginTypeEnum;
 import org.springblade.auth.utils.TokenUtil;
 import org.springblade.common.cache.CacheNames;
 import org.springblade.core.jwt.JwtUtil;
 import org.springblade.core.jwt.props.JwtProperties;
 import org.springblade.core.redis.cache.BladeRedis;
 import org.springblade.core.tool.api.R;
-import org.springblade.core.tool.utils.*;
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.system.cache.ParamCache;
 import org.springblade.system.entity.Tenant;
 import org.springblade.system.feign.ISysClient;
@@ -54,162 +57,167 @@ import java.util.List;
 @AllArgsConstructor
 public class BladeUserDetailsServiceImpl implements UserDetailsService {
 
-	public static final Integer FAIL_COUNT = 5;
-	public static final String FAIL_COUNT_VALUE = "account.failCount";
-
-	private final IUserClient userClient;
-	private final ISysClient sysClient;
-
-	private final BladeRedis bladeRedis;
-	private final JwtProperties jwtProperties;
-
-	@Override
-	@SneakyThrows
-	public BladeUserDetails loadUserByUsername(String username) {
-		HttpServletRequest request = WebUtil.getRequest();
-		// 获取用户绑定ID
-		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
-		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
-		// 获取租户ID
-		String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
-		String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
-
-		Object passwordObj = request.getAttribute(TokenUtil.PASSWORD_KEY);
-		String password = passwordObj == null ? "" : passwordObj.toString();
-
-		String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
-		// 判断租户请求头
-		if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
-			throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
-		}
-		// 判断令牌合法性
-		if (!judgeRefreshToken(grantType, request)) {
-			throw new UserDeniedAuthorizationException(TokenUtil.TOKEN_NOT_PERMISSION);
-		}
-
-		// 指定租户ID
-		String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
-		// 判断登录是否锁定
-		int count = getFailCount(tenantId, username);
-		int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
-		if (count >= failCount) {
-			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
-		}
-
-		// 获取租户信息
-		R<Tenant> tenant = sysClient.getTenant(tenantId);
-		if (tenant.isSuccess()) {
-			if (TokenUtil.judgeTenant(tenant.getData())) {
-				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
-			}
-		} else {
-			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
-		}
-
-		// 获取用户类型
-		String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
-
-		// 远程调用返回数据
-		R<UserInfo> result;
-		// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
-		if (userType.equals(UserEnum.WEB.getName())) {
-			result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
-		} else if (userType.equals(UserEnum.APP.getName())) {
-			result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
-		} else {
-			result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
-		}
-
-		// 判断返回信息
-		if (result.isSuccess()) {
-			UserInfo userInfo = result.getData();
-			User user = userInfo.getUser();
-			// 用户不存在,但提示用户名与密码错误并锁定账号
-			if (user == null || user.getId() == null) {
-				setFailCount(tenantId, username, count);
-				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
-			}
-			// 用户存在但密码错误,超过次数则锁定账号
-			if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
-				setFailCount(tenantId, username, count);
-				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
-			}
-			// 用户角色不存在
-			if (Func.isEmpty(userInfo.getRoles())) {
-				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
-			}
-			// 多部门情况下指定单部门
-			if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
-				user.setDeptId(headerDept);
-			}
-			// 多角色情况下指定单角色
-			if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
-				R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
-				if (roleResult.isSuccess()) {
-					userInfo.setRoles(roleResult.getData());
-				}
-				user.setRoleId(headerRole);
-			}
-			// 成功则清除登录错误次数
-			delFailCount(tenantId, username);
-			return new BladeUserDetails(user.getId(),
-				user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
-				username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
-				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
-		} else {
-			throw new UsernameNotFoundException(result.getMsg());
-		}
-	}
-
-	/**
-	 * 获取账号错误次数
-	 *
-	 * @param tenantId 租户id
-	 * @param username 账号
-	 * @return int
-	 */
-	private int getFailCount(String tenantId, String username) {
-		return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
-	}
-
-	/**
-	 * 设置账号错误次数
-	 *
-	 * @param tenantId 租户id
-	 * @param username 账号
-	 * @param count    次数
-	 */
-	private void setFailCount(String tenantId, String username, int count) {
-		bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(10));
-	}
-
-	/**
-	 * 清空账号错误次数
-	 *
-	 * @param tenantId 租户id
-	 * @param username 账号
-	 */
-	private void delFailCount(String tenantId, String username) {
-		bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
-	}
-
-	/**
-	 * 校验refreshToken合法性
-	 *
-	 * @param grantType 认证类型
-	 * @param request   请求
-	 */
-	private boolean judgeRefreshToken(String grantType, HttpServletRequest request) {
-		if (jwtProperties.getState() && jwtProperties.getSingle() && StringUtil.equals(grantType, TokenUtil.REFRESH_TOKEN_KEY)) {
-			String refreshToken = request.getParameter(TokenUtil.REFRESH_TOKEN_KEY);
-			Claims claims = JwtUtil.parseJWT(refreshToken);
-			String tenantId = String.valueOf(claims.get("tenant_id"));
-			String userId = String.valueOf(claims.get("user_id"));
-			String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
-			return StringUtil.equalsIgnoreCase(token, refreshToken);
-		}
-		return true;
-	}
+    public static final Integer FAIL_COUNT = 5;
+    public static final String FAIL_COUNT_VALUE = "account.failCount";
+
+    private final IUserClient userClient;
+    private final ISysClient sysClient;
+
+    private final BladeRedis bladeRedis;
+    private final JwtProperties jwtProperties;
+
+    private final HttpServletRequest request;
+
+    @Override
+    public BladeUserDetails loadUserByUsername(String username) {
+        String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+        String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+        String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
+        String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
+        String password = request.getParameter(TokenUtil.PASSWORD_KEY);
+        String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
+
+        // 判断租户请求头
+        if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
+            throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
+        }
+        // 判断令牌合法性
+        if (!judgeRefreshToken(grantType, request)) {
+            throw new UserDeniedAuthorizationException(TokenUtil.TOKEN_NOT_PERMISSION);
+        }
+
+        // 指定租户ID
+        String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
+        // 判断登录是否锁定
+        int count = getFailCount(tenantId, username);
+        int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
+        if (count >= failCount) {
+            throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+        }
+
+        // 获取租户信息
+        R<Tenant> tenant = sysClient.getTenant(tenantId);
+        if (tenant.isSuccess()) {
+            if (TokenUtil.judgeTenant(tenant.getData())) {
+                throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+            }
+        } else {
+            throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+        }
+
+        // 获取用户类型
+        String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+        // 远程调用返回数据
+        R<UserInfo> result;
+        // 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+        if (userType.equals(UserEnum.WEB.getName())) {
+            result = userClient.userInfo(tenantId, username, UserEnum.WEB.getName());
+        } else if (userType.equals(UserEnum.APP.getName())) {
+            result = userClient.userInfo(tenantId, username, UserEnum.APP.getName());
+        } else {
+            result = userClient.userInfo(tenantId, username, UserEnum.OTHER.getName());
+        }
+
+        // 判断返回信息
+        if (result.isSuccess()) {
+            UserInfo userInfo = result.getData();
+            User user = userInfo.getUser();
+            // 用户不存在,但提示用户名与密码错误并锁定账号
+            if (user == null || user.getId() == null) {
+                setFailCount(tenantId, username, count);
+                throw new UserDeniedAuthorizationException(TokenUtil.USER_NOT_FOUND);
+            }
+
+            if (!LoginTypeEnum.getNoPasswordRequiredTypeList().contains(LoginTypeEnum.get(request.getParameter("type")))) {
+                // 用户存在但密码错误,超过次数则锁定账号
+                if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY) && !user.getPassword().equals(DigestUtil.hex(password))) {
+                    setFailCount(tenantId, username, count);
+                    throw new UserDeniedAuthorizationException(TokenUtil.USER_NOT_FOUND);
+                }
+            }
+
+            // 用户角色不存在
+            if (Func.isEmpty(userInfo.getRoles())) {
+                throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
+            }
+
+            // 多部门情况下指定单部门
+            if (Func.isNotEmpty(headerDept) && user.getDeptId().contains(headerDept)) {
+                user.setDeptId(headerDept);
+            }
+
+            // 多角色情况下指定单角色
+            if (Func.isNotEmpty(headerRole) && user.getRoleId().contains(headerRole)) {
+                R<List<String>> roleResult = sysClient.getRoleAliases(headerRole);
+                if (roleResult.isSuccess()) {
+                    userInfo.setRoles(roleResult.getData());
+                }
+                user.setRoleId(headerRole);
+            }
+
+            // 成功则清除登录错误次数
+            delFailCount(tenantId, username);
+            return new BladeUserDetails(user.getId(),
+                    user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(userInfo.getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+                    username, AuthConstant.ENCRYPT + user.getPassword(), userInfo.getDetail(), true, true, true, true,
+                    AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+
+        } else {
+            throw new UsernameNotFoundException(result.getMsg());
+        }
+
+    }
+
+    /**
+     * 获取账号错误次数
+     *
+     * @param tenantId 租户id
+     * @param username 账号
+     * @return int
+     */
+    private int getFailCount(String tenantId, String username) {
+        return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+    }
+
+    /**
+     * 设置账号错误次数
+     *
+     * @param tenantId 租户id
+     * @param username 账号
+     * @param count    次数
+     */
+    private void setFailCount(String tenantId, String username, int count) {
+        bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(10));
+    }
+
+    /**
+     * 清空账号错误次数
+     *
+     * @param tenantId 租户id
+     * @param username 账号
+     */
+    private void delFailCount(String tenantId, String username) {
+        bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
+    }
+
+    /**
+     * 校验refreshToken合法性
+     *
+     * @param grantType 认证类型
+     * @param request   请求
+     */
+    private boolean judgeRefreshToken(String grantType, HttpServletRequest request) {
+        if (jwtProperties.getState() && jwtProperties.getSingle() && StringUtil.equals(grantType, TokenUtil.REFRESH_TOKEN_KEY)) {
+            String refreshToken = request.getParameter(TokenUtil.REFRESH_TOKEN_KEY);
+            Claims claims = JwtUtil.parseJWT(refreshToken);
+            String tenantId = String.valueOf(claims.get("tenant_id"));
+            String userId = String.valueOf(claims.get("user_id"));
+            String token = JwtUtil.getRefreshToken(tenantId, userId, refreshToken);
+            return StringUtil.equalsIgnoreCase(token, refreshToken);
+        }
+        return true;
+    }
 
 
 }

+ 26 - 0
bladex/blade-auth/src/main/java/org/springblade/auth/service/MyAuthenticationProvider.java

@@ -0,0 +1,26 @@
+package org.springblade.auth.service;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Component;
+
+/**
+ * 在BladeUserDetailsServiceImpl中的loadUserByUsername方法已进行密码校验
+ * 因此跳过security框架中的密码校验,防止无需密码登录的类型无法登录
+ */
+@Component
+public class MyAuthenticationProvider extends DaoAuthenticationProvider {
+
+    public MyAuthenticationProvider(UserDetailsService userDetailsService) {
+        setUserDetailsService(userDetailsService);
+    }
+
+    @Override
+    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
+        // super.additionalAuthenticationChecks(userDetails, authentication);
+    }
+
+}

+ 79 - 0
bladex/blade-auth/src/main/java/org/springblade/auth/utils/JwtUtil.java

@@ -0,0 +1,79 @@
+package org.springblade.auth.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.security.SignatureException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.Key;
+import java.util.Date;
+
+public class JwtUtil {
+
+    private static final String SECRET = "sbajhdbasjk342@ldhbasilheiuqwhei$Fu123y28db@iaSARFSRFAXSksZSXb%dkaSADaSSAsb";
+    private static final Key signingKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8),
+            SignatureAlgorithm.HS256.getJcaName());
+
+    /**
+     * 生成token
+     *
+     * @param obj 加密对象
+     * @return token
+     */
+    public static String generateToken(Object obj) {
+        // 这里其实就是new一个JwtBuilder,设置jwt的body
+        JwtBuilder jwtBuilder = Jwts.builder()
+                // 设置JwtId:是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
+                .setId(IdWorker.getIdStr())
+                // 设置发送内容
+                .setSubject(JSONObject.toJSONString(obj))
+                // 签发时间
+                .setIssuedAt(new Date())
+                // 设置过期时间
+                .setExpiration(getExpiration())
+                // 签名
+                .signWith(signingKey);
+
+        // 根据对象生成 令牌token
+        return jwtBuilder.compact();
+    }
+
+    /**
+     * 解析token
+     *
+     * @param token  token
+     * @param tClass 反序列化对象
+     */
+    public static <T> T parseToken(String token, Class<T> tClass) {
+        try {
+            Claims claims = Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(token).getBody();
+            return JSONObject.parseObject(claims.getSubject(), tClass);
+        } catch (ExpiredJwtException expiredJwtException) {
+            throw new UserDeniedAuthorizationException("token已过期");
+        } catch (SignatureException signatureException) {
+            throw new UserDeniedAuthorizationException("密钥错误");
+        } catch (MalformedJwtException malformedJwtException) {
+            throw new UserDeniedAuthorizationException("密钥算法,或者密钥转换错误,或者token不正确");
+        } catch (MissingClaimException missingClaimException) {
+            throw new UserDeniedAuthorizationException("密钥缺少校验数据");
+        } catch (JwtException jwtException) {
+            throw new UserDeniedAuthorizationException("密钥解析错误");
+        }
+    }
+
+    /**
+     * 设置过期时间
+     * <p>
+     * 60秒后过期
+     */
+    static private Date getExpiration() {
+        return new Date(System.currentTimeMillis() + 60 * 1000);
+    }
+
+}
+
+
+

+ 1 - 2
bladex/blade-service-api/blade-user-api/src/main/java/org/springblade/system/user/entity/User.java

@@ -93,9 +93,8 @@ public class User extends TenantEntity {
     private String postId;
 
     /**
-     * 微信小程序字段
+     * 微信小程序openId
      */
-    private String appletKey;
     private String appletOpenId;
 
 }

+ 1 - 2
bladex/blade-service/blade-user/src/main/java/org/springblade/system/user/service/impl/UserServiceImpl.java

@@ -442,8 +442,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
             Wrappers.<User>lambdaUpdate()
                     .eq(User::getAppletOpenId, user.getAppletOpenId())
                     .ne(BaseEntity::getId, user.getId())
-                    .set(User::getAppletOpenId, null)
-                    .set(User::getAppletKey, null);
+                    .set(User::getAppletOpenId, null);
         }
 
     }

+ 9 - 3
hx-service/blade-ex/src/main/java/com/fjhx/controller/TenantController.java

@@ -18,6 +18,7 @@ package com.fjhx.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fjhx.base.Condition;
+import com.fjhx.params.TenantVo;
 import com.fjhx.service.ITenantService;
 import lombok.AllArgsConstructor;
 import org.springblade.core.boot.ctrl.BladeController;
@@ -57,6 +58,9 @@ public class TenantController extends BladeController {
         return R.success();
     }
 
+    /**
+     * 分页
+     */
     @PostMapping("/page")
     @PreAuth(RoleConstant.HAS_ROLE_ADMINISTRATOR)
     public R page(@RequestBody Condition condition) {
@@ -64,11 +68,13 @@ public class TenantController extends BladeController {
         return R.success(page);
     }
 
+    /**
+     * 第三方创建租户
+     */
     @PostMapping("/thirdPartyCreate")
     public R thirdPartyCreate(@RequestBody Condition condition) {
-        tenantService.thirdPartyCreate(condition.getStr("token"));
-        return R.success();
+        TenantVo token = tenantService.thirdPartyCreate(condition.getStr("token"));
+        return R.success(token);
     }
 
-
 }

+ 2 - 1
hx-service/blade-ex/src/main/java/com/fjhx/service/ITenantService.java

@@ -18,6 +18,7 @@ package com.fjhx.service;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fjhx.base.Condition;
+import com.fjhx.params.TenantVo;
 import org.springblade.core.mp.base.BaseService;
 import org.springblade.system.entity.Tenant;
 
@@ -43,6 +44,6 @@ public interface ITenantService extends BaseService<Tenant> {
     /**
      * 第三方创建租户
      */
-    void thirdPartyCreate(String token);
+    TenantVo thirdPartyCreate(String token);
 
 }

+ 8 - 7
hx-service/blade-ex/src/main/java/com/fjhx/service/impl/TenantServiceImpl.java

@@ -167,8 +167,9 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, Tenant> imp
         return page;
     }
 
+    @Transactional(rollbackFor = Exception.class)
     @Override
-    public void thirdPartyCreate(String token) {
+    public TenantVo thirdPartyCreate(String token) {
         TenantVo tenantVo = JwtUtil.parseToken(token, TenantVo.class);
 
         String tenantId = tenantVo.getTenantId();
@@ -180,16 +181,15 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, Tenant> imp
         String account = tenantVo.getAccount();
         Assert.notEmpty(account, "用户账号不能为空");
 
-        Tenant tenant = BeanUtil.toBean(tenantVo, Tenant.class);
-        Long count = count(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenant));
-        Assert.eqZero(count, "租户id已存在");
-
-        tenant.setLinkman(tenant.getLinkman() == null ? tenantName : tenant.getLinkman());
-
+        tenantVo.setLinkman(tenantVo.getLinkman() == null ? tenantName : tenantVo.getLinkman());
         tenantVo.setName(tenantVo.getName() == null ? tenantName : tenantVo.getName());
         tenantVo.setRealName(tenantVo.getRealName() == null ? tenantName : tenantVo.getRealName());
         tenantVo.setPassword(tenantVo.getPassword() == null ? RandomUtil.randomString(6) : tenantVo.getPassword());
 
+        Tenant tenant = BeanUtil.toBean(tenantVo, Tenant.class);
+        Long count = count(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getTenantId, tenant));
+        Assert.eqZero(count, "租户id已存在");
+
         // 指定账号额度20
         tenant.setAccountNumber(20);
 
@@ -213,6 +213,7 @@ public class TenantServiceImpl extends BaseServiceImpl<TenantMapper, Tenant> imp
 
         save(tenant);
 
+        return tenantVo;
     }
 
     /**

+ 1 - 17
hx-service/blade-ex/src/main/java/com/fjhx/utils/JwtUtil.java

@@ -10,8 +10,6 @@ import javax.crypto.spec.SecretKeySpec;
 import java.nio.charset.StandardCharsets;
 import java.security.Key;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
 
 public class JwtUtil {
 
@@ -19,20 +17,6 @@ public class JwtUtil {
     private static final Key signingKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8),
             SignatureAlgorithm.HS256.getJcaName());
 
-    public static void main(String[] args) {
-
-        HashMap<String, Object> map = new HashMap<>();
-        map.put("test", "dasdsaasd");
-        map.put("test2", "213124asda");
-        String test = generateToken(map);
-
-        System.out.println(test);
-        System.out.println(test.length());
-
-        Map<String, Object> map1 = parseToken(test, Map.class);
-        System.out.println(map1);
-    }
-
     /**
      * 生成token
      *
@@ -86,7 +70,7 @@ public class JwtUtil {
      * 30秒后过期
      */
     static private Date getExpiration() {
-        return new Date(System.currentTimeMillis() + 30 * 1000);
+        return new Date(System.currentTimeMillis() + 60 * 1000);
     }
 
 }

+ 11 - 0
hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/HxClientConstant.java

@@ -0,0 +1,11 @@
+package com.fjhx.demo;
+
+public interface HxClientConstant {
+
+    // 前缀
+    String URL_PREFIX = "http://localhost:8080/";
+
+    // 生成租户
+    String CREATE_TENANT_URL = URL_PREFIX + "blade-ex/tenant/thirdPartyCreate";
+
+}

+ 55 - 0
hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/HxClientUtil.java

@@ -0,0 +1,55 @@
+package com.fjhx.demo;
+
+import com.fjhx.demo.entity.CreateTenantVo;
+import com.fjhx.demo.entity.ResultEntity;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.web.client.ResourceAccessException;
+import org.springframework.web.client.ResponseErrorHandler;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.HashMap;
+
+public class HxClientUtil {
+
+    private static final RestTemplate restTemplate = new RestTemplate();
+
+    static {
+        restTemplate.setErrorHandler(new ResponseErrorHandler() {
+            @Override
+            public boolean hasError(@NotNull ClientHttpResponse response) {
+                return false;
+            }
+
+            @Override
+            public void handleError(@NotNull ClientHttpResponse response) {
+
+            }
+        });
+    }
+
+    private HxClientUtil() {
+    }
+
+//    public static void main(String[] args) {
+//        CreateTenantVo createTenantVo = new CreateTenantVo();
+//        createTenantVo.setTenantId("test2");
+//        createTenantVo.setAccount("admin");
+//        createTenantVo.setTenantName("测试租户");
+//        ResultEntity<CreateTenantVo> result = createTenant(createTenantVo);
+//        System.out.println(result);
+//    }
+
+    /**
+     * 生成租户
+     *
+     * @param vo 租户信息
+     * @return 租户信息
+     */
+    public static ResultEntity<CreateTenantVo> createTenant(CreateTenantVo vo) throws ResourceAccessException {
+        HashMap<Object, Object> map = new HashMap<>();
+        map.put("token", JwtUtil.generateToken(vo));
+        return restTemplate.postForEntity(HxClientConstant.CREATE_TENANT_URL, map, ResultEntity.class).getBody();
+    }
+
+}

+ 79 - 0
hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/JwtUtil.java

@@ -0,0 +1,79 @@
+package com.fjhx.demo;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.security.SignatureException;
+import org.springblade.core.log.exception.ServiceException;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.Key;
+import java.util.Date;
+
+public class JwtUtil {
+
+    private static final String SECRET = "sbajhdbasjk342@ldhbasilheiuqwhei$Fu123y28db@iaSARFSRFAXSksZSXb%dkaSADaSSAsb";
+    private static final Key signingKey = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8),
+            SignatureAlgorithm.HS256.getJcaName());
+
+    /**
+     * 生成token
+     *
+     * @param obj 加密对象
+     * @return token
+     */
+    public static String generateToken(Object obj) {
+        // 这里其实就是new一个JwtBuilder,设置jwt的body
+        JwtBuilder jwtBuilder = Jwts.builder()
+                // 设置JwtId:是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
+                .setId(IdWorker.getIdStr())
+                // 设置发送内容
+                .setSubject(JSONObject.toJSONString(obj))
+                // 签发时间
+                .setIssuedAt(new Date())
+                // 设置过期时间
+                .setExpiration(getExpiration())
+                // 签名
+                .signWith(signingKey);
+
+        // 根据对象生成 令牌token
+        return jwtBuilder.compact();
+    }
+
+    /**
+     * 解析token
+     *
+     * @param token  token
+     * @param tClass 反序列化对象
+     */
+    public static <T> T parseToken(String token, Class<T> tClass) {
+        try {
+            Claims claims = Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(token).getBody();
+            return JSONObject.parseObject(claims.getSubject(), tClass);
+        } catch (ExpiredJwtException expiredJwtException) {
+            throw new ServiceException("token已过期");
+        } catch (SignatureException signatureException) {
+            throw new ServiceException("密钥错误");
+        } catch (MalformedJwtException malformedJwtException) {
+            throw new ServiceException("密钥算法,或者密钥转换错误,或者token不正确");
+        } catch (MissingClaimException missingClaimException) {
+            throw new ServiceException("密钥缺少校验数据");
+        } catch (JwtException jwtException) {
+            throw new ServiceException("密钥解析错误");
+        }
+    }
+
+    /**
+     * 设置过期时间
+     * <p>
+     * 45秒后过期
+     */
+    static private Date getExpiration() {
+        return new Date(System.currentTimeMillis() + 60 * 1000);
+    }
+
+}
+
+
+

+ 51 - 0
hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/entity/CreateTenantVo.java

@@ -0,0 +1,51 @@
+package com.fjhx.demo.entity;
+
+import lombok.Data;
+
+/**
+ * 租户信息
+ */
+@Data
+public class CreateTenantVo {
+
+    /**
+     * 租户id(必填)
+     */
+    private String tenantId;
+
+    /**
+     * 租户名称(必填)
+     */
+    private String tenantName;
+
+    /**
+     * 联系人(非必填,为空时用租户名称赋值)
+     */
+    private String linkman;
+
+    /**
+     * 联系电话(非必填)
+     */
+    private String contactNumber;
+
+    /**
+     * 昵称(非必填,为空时用租户名称赋值)
+     */
+    private String name;
+
+    /**
+     * 真名(非必填,为空时用租户名称赋值)
+     */
+    private String realName;
+
+    /**
+     * 账号(必填)
+     */
+    private String account;
+
+    /**
+     * 密码(非必填,为空时生成随机6位数密码)
+     */
+    private String password;
+
+}

+ 28 - 0
hx-service/foreign-trade-superman/src/main/java/com/fjhx/demo/entity/ResultEntity.java

@@ -0,0 +1,28 @@
+package com.fjhx.demo.entity;
+
+import lombok.Data;
+
+@Data
+public class ResultEntity<T> {
+
+    /**
+     * 状态码
+     */
+    private int code;
+
+    /**
+     * 是否成功
+     */
+    private boolean success;
+
+    /**
+     * 说明
+     */
+    private String msg;
+
+    /**
+     * 承载数据
+     */
+    private T data;
+
+}