From 184cf97304970c3138a42c370bf0ea0f254b667e Mon Sep 17 00:00:00 2001 From: EightMonth Date: Tue, 16 Jan 2024 19:09:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=93=E9=80=9A=E4=B8=89=E6=96=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/增量SQL/sas升级脚本.sql | 2 +- .../org/jeecg/config/security/LoginType.java | 5 + .../jeecg/config/security/SecurityConfig.java | 16 +- .../app/AppGrantAuthenticationProvider.java | 12 +- .../PhoneGrantAuthenticationConvert.java | 3 +- .../PhoneGrantAuthenticationProvider.java | 12 +- .../SocialGrantAuthenticationConvert.java | 80 ++++++ .../SocialGrantAuthenticationProvider.java | 253 ++++++++++++++++++ .../SocialGrantAuthenticationToken.java | 20 ++ pom.xml | 9 +- 10 files changed, 395 insertions(+), 17 deletions(-) create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationConvert.java create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationProvider.java create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationToken.java diff --git a/db/增量SQL/sas升级脚本.sql b/db/增量SQL/sas升级脚本.sql index d0e295257..6417a915b 100644 --- a/db/增量SQL/sas升级脚本.sql +++ b/db/增量SQL/sas升级脚本.sql @@ -37,7 +37,7 @@ now(), null, '3eacac0e-0de9-4727-9a64-6bdd4be2ee1f', 'client_secret_basic', -'refresh_token,authorization_code,password', +'refresh_token,authorization_code,password,app,phone,social', 'http://127.0.0.1:8080/jeecg-', 'http://127.0.0.1:8080/', '*', diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/LoginType.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/LoginType.java index 06e59a8a3..c5ee96206 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/LoginType.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/LoginType.java @@ -28,4 +28,9 @@ public class LoginType { * 扫码登录 */ public static final String SCAN = "scan"; + + /** + * 所有联合登录,比如github\钉钉\企业微信\微信 + */ + public static final String SOCIAL = "social"; } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/SecurityConfig.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/SecurityConfig.java index 809cca92b..7f22a93ec 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/SecurityConfig.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/SecurityConfig.java @@ -6,8 +6,14 @@ import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; import lombok.AllArgsConstructor; +import org.jeecg.config.security.app.AppGrantAuthenticationConvert; +import org.jeecg.config.security.app.AppGrantAuthenticationProvider; import org.jeecg.config.security.password.PasswordGrantAuthenticationConvert; import org.jeecg.config.security.password.PasswordGrantAuthenticationProvider; +import org.jeecg.config.security.phone.PhoneGrantAuthenticationConvert; +import org.jeecg.config.security.phone.PhoneGrantAuthenticationProvider; +import org.jeecg.config.security.social.SocialGrantAuthenticationConvert; +import org.jeecg.config.security.social.SocialGrantAuthenticationProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -17,6 +23,7 @@ import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.jwt.JwtDecoder; @@ -47,7 +54,7 @@ import java.util.UUID; */ @Configuration @EnableWebSecurity -@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true) +@EnableMethodSecurity @AllArgsConstructor public class SecurityConfig { @@ -62,6 +69,12 @@ public class SecurityConfig { http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenRequestConverter(new PasswordGrantAuthenticationConvert()) .authenticationProvider(new PasswordGrantAuthenticationProvider(authorizationService, tokenGenerator()))) + .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenRequestConverter(new PhoneGrantAuthenticationConvert()) + .authenticationProvider(new PhoneGrantAuthenticationProvider(authorizationService, tokenGenerator()))) + .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenRequestConverter(new AppGrantAuthenticationConvert()) + .authenticationProvider(new AppGrantAuthenticationProvider(authorizationService, tokenGenerator()))) + .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenRequestConverter(new SocialGrantAuthenticationConvert()) + .authenticationProvider(new SocialGrantAuthenticationProvider(authorizationService, tokenGenerator()))) //开启OpenID Connect 1.0(其中oidc为OpenID Connect的缩写)。 访问 /.well-known/openid-configuration即可获取认证信息 .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 http @@ -153,6 +166,7 @@ public class SecurityConfig { config.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); return config; })) + .csrf(AbstractHttpConfigurer::disable) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/app/AppGrantAuthenticationProvider.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/app/AppGrantAuthenticationProvider.java index 29ef8b127..2d4034015 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/app/AppGrantAuthenticationProvider.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/app/AppGrantAuthenticationProvider.java @@ -68,11 +68,11 @@ public class AppGrantAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - PasswordGrantAuthenticationToken passwordGrantAuthenticationToken = (PasswordGrantAuthenticationToken) authentication; - Map additionalParameter = passwordGrantAuthenticationToken.getAdditionalParameters(); + AppGrantAuthenticationToken appGrantAuthenticationToken = (AppGrantAuthenticationToken) authentication; + Map additionalParameter = appGrantAuthenticationToken.getAdditionalParameters(); // 授权类型 - AuthorizationGrantType authorizationGrantType = passwordGrantAuthenticationToken.getGrantType(); + AuthorizationGrantType authorizationGrantType = appGrantAuthenticationToken.getGrantType(); // 用户名 String username = (String) additionalParameter.get(OAuth2ParameterNames.USERNAME); // 密码 @@ -105,7 +105,7 @@ public class AppGrantAuthenticationProvider implements AuthenticationProvider { throw new JeecgCaptchaException(HttpStatus.PRECONDITION_FAILED.value(), "验证码错误"); } - OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(appGrantAuthenticationToken); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) { @@ -132,7 +132,7 @@ public class AppGrantAuthenticationProvider implements AuthenticationProvider { .authorizationServerContext(AuthorizationServerContextHolder.getContext()) .authorizationGrantType(authorizationGrantType) .authorizedScopes(requestScopeSet) - .authorizationGrant(passwordGrantAuthenticationToken); + .authorizationGrant(appGrantAuthenticationToken); OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient) .principalName(clientPrincipal.getName()) @@ -212,7 +212,7 @@ public class AppGrantAuthenticationProvider implements AuthenticationProvider { @Override public boolean supports(Class authentication) { - return PasswordGrantAuthenticationToken.class.isAssignableFrom(authentication); + return AppGrantAuthenticationToken.class.isAssignableFrom(authentication); } private static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) { diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationConvert.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationConvert.java index 85f4b14f3..66d929275 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationConvert.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationConvert.java @@ -36,8 +36,7 @@ public class PhoneGrantAuthenticationConvert implements AuthenticationConverter // 验证码 String captcha = parameters.getFirst("captcha"); - if (!StringUtils.hasText(captcha) || - parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) { + if (!StringUtils.hasText(captcha)) { throw new OAuth2AuthenticationException("无效请求,验证码不能为空!"); } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationProvider.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationProvider.java index e2ed2355c..08cd27f1b 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationProvider.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/phone/PhoneGrantAuthenticationProvider.java @@ -68,11 +68,11 @@ public class PhoneGrantAuthenticationProvider implements AuthenticationProvider @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - PasswordGrantAuthenticationToken passwordGrantAuthenticationToken = (PasswordGrantAuthenticationToken) authentication; - Map additionalParameter = passwordGrantAuthenticationToken.getAdditionalParameters(); + PhoneGrantAuthenticationToken phoneGrantAuthenticationToken = (PhoneGrantAuthenticationToken) authentication; + Map additionalParameter = phoneGrantAuthenticationToken.getAdditionalParameters(); // 授权类型 - AuthorizationGrantType authorizationGrantType = passwordGrantAuthenticationToken.getGrantType(); + AuthorizationGrantType authorizationGrantType = phoneGrantAuthenticationToken.getGrantType(); // 手机号 String phone = (String) additionalParameter.get("mobile"); @@ -102,7 +102,7 @@ public class PhoneGrantAuthenticationProvider implements AuthenticationProvider throw new JeecgBootException("手机验证码错误"); } - OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(phoneGrantAuthenticationToken); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) { @@ -118,7 +118,7 @@ public class PhoneGrantAuthenticationProvider implements AuthenticationProvider .authorizationServerContext(AuthorizationServerContextHolder.getContext()) .authorizationGrantType(authorizationGrantType) .authorizedScopes(requestScopeSet) - .authorizationGrant(passwordGrantAuthenticationToken); + .authorizationGrant(phoneGrantAuthenticationToken); OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient) .principalName(clientPrincipal.getName()) @@ -195,7 +195,7 @@ public class PhoneGrantAuthenticationProvider implements AuthenticationProvider @Override public boolean supports(Class authentication) { - return PasswordGrantAuthenticationToken.class.isAssignableFrom(authentication); + return PhoneGrantAuthenticationToken.class.isAssignableFrom(authentication); } private static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) { diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationConvert.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationConvert.java new file mode 100644 index 000000000..67a43bd3f --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationConvert.java @@ -0,0 +1,80 @@ +package org.jeecg.config.security.social; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import org.jeecg.config.security.LoginType; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author EightMonth + * @date 2024/1/1 + */ +@AllArgsConstructor +public class SocialGrantAuthenticationConvert implements AuthenticationConverter { + @Override + public Authentication convert(HttpServletRequest request) { + + String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); + if (!LoginType.SOCIAL.equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + + //从request中提取请求参数,然后存入MultiValueMap + MultiValueMap parameters = getParameters(request); + + String token = parameters.getFirst("token"); + if (!StringUtils.hasText(token)) { + throw new OAuth2AuthenticationException("无效请求,三方token不能为空!"); + } + + String source = parameters.getFirst("thirdType"); + if (!StringUtils.hasText(source)) { + throw new OAuth2AuthenticationException("无效请求,三方来源不能为空!"); + } + + //收集要传入PhoneGrantAuthenticationToken构造方法的参数, + //该参数接下来在PhoneGrantAuthenticationProvider中使用 + Map additionalParameters = new HashMap<>(); + //遍历从request中提取的参数,排除掉grant_type、client_id、code等字段参数,其他参数收集到additionalParameters中 + parameters.forEach((key, value) -> { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && + !key.equals(OAuth2ParameterNames.CLIENT_ID) && + !key.equals(OAuth2ParameterNames.CODE)) { + additionalParameters.put(key, value.get(0)); + } + }); + + //返回自定义的PhoneGrantAuthenticationToken对象 + return new SocialGrantAuthenticationToken(clientPrincipal, additionalParameters); + + } + + /** + *从request中提取请求参数,然后存入MultiValueMap + */ + private static MultiValueMap getParameters(HttpServletRequest request) { + Map parameterMap = request.getParameterMap(); + MultiValueMap parameters = new LinkedMultiValueMap<>(parameterMap.size()); + parameterMap.forEach((key, values) -> { + if (values.length > 0) { + for (String value : values) { + parameters.add(key, value); + } + } + }); + return parameters; + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationProvider.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationProvider.java new file mode 100644 index 000000000..9371dcd4a --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationProvider.java @@ -0,0 +1,253 @@ +package org.jeecg.config.security.social; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.interfaces.DecodedJWT; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.CommonAPI; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.exception.JeecgBootException; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.common.system.vo.SysDepartModel; +import org.jeecg.common.util.RedisUtil; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.config.JeecgBaseConfig; +import org.jeecg.config.security.password.PasswordGrantAuthenticationToken; +import org.jeecg.modules.base.service.BaseCommonService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.*; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; +import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; +import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext; +import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; +import org.springframework.util.Assert; + +import java.security.Principal; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author EightMonth + * @date 2024/1/1 + */ +@Slf4j +public class SocialGrantAuthenticationProvider implements AuthenticationProvider { + + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; + @Autowired + private CommonAPI commonAPI; + @Autowired + private RedisUtil redisUtil; + @Autowired + private JeecgBaseConfig jeecgBaseConfig; + @Autowired + private BaseCommonService baseCommonService; + + public SocialGrantAuthenticationProvider(OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator tokenGenerator) { + Assert.notNull(authorizationService, "authorizationService cannot be null"); + Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); + this.authorizationService = authorizationService; + this.tokenGenerator = tokenGenerator; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + SocialGrantAuthenticationToken socialGrantAuthenticationToken = (SocialGrantAuthenticationToken) authentication; + Map additionalParameter = socialGrantAuthenticationToken.getAdditionalParameters(); + + // 授权类型 + AuthorizationGrantType authorizationGrantType = socialGrantAuthenticationToken.getGrantType(); + // 三方token + String token = (String) additionalParameter.get("token"); + // 三方来源 + String source = (String) additionalParameter.get("thirdType"); + + //请求参数权限范围 + String requestScopesStr = (String)additionalParameter.getOrDefault(OAuth2ParameterNames.SCOPE, "*"); + //请求参数权限范围专场集合 + Set requestScopeSet = Stream.of(requestScopesStr.split(" ")).collect(Collectors.toSet()); + + DecodedJWT jwt = JWT.decode(token); + String username = jwt.getClaim("username").asString(); + + LoginUser loginUser = commonAPI.getUserByName(username); + // 检查用户可行性 + checkUserIsEffective(loginUser); + + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(socialGrantAuthenticationToken); + RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); + + if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) { + throw new JeecgBootException("非法登录"); + } + + //由于在上面已验证过用户名、密码,现在构建一个已认证的对象UsernamePasswordAuthenticationToken + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken.authenticated(loginUser,clientPrincipal,new ArrayList<>()); + + DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder() + .registeredClient(registeredClient) + .principal(usernamePasswordAuthenticationToken) + .authorizationServerContext(AuthorizationServerContextHolder.getContext()) + .authorizationGrantType(authorizationGrantType) + .authorizedScopes(requestScopeSet) + .authorizationGrant(socialGrantAuthenticationToken); + + OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient) + .principalName(clientPrincipal.getName()) + .authorizedScopes(requestScopeSet) + .attribute(Principal.class.getName(), loginUser.getUsername()) + .authorizationGrantType(authorizationGrantType); + + + // ----- Access token ----- + OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build(); + OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext); + if (generatedAccessToken == null) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "无法生成访问token,请联系管理系。", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(), + generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); + if (generatedAccessToken instanceof ClaimAccessor) { + authorizationBuilder.token(accessToken, (metadata) -> { + metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()); + }); + } else { + authorizationBuilder.accessToken(accessToken); + } + + // ----- Refresh token ----- + OAuth2RefreshToken refreshToken = null; + if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) && + // 不向公共客户端颁发刷新令牌 + !clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) { + + tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build(); + OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext); + if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, + "无法生成刷新token,请联系管理员。", ERROR_URI); + throw new OAuth2AuthenticationException(error); + } + + refreshToken = (OAuth2RefreshToken) generatedRefreshToken; + authorizationBuilder.refreshToken(refreshToken); + } + + OAuth2Authorization authorization = authorizationBuilder.build(); + + authorizationService.save(authorization); + + baseCommonService.addLog("用户名: " + loginUser.getUsername() + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser); + + Map addition = new HashMap<>(); + // 设置登录用户信息 + addition.put("userInfo", loginUser); + addition.put("sysAllDictItems", commonAPI.queryAllDictItems()); + + List departs = commonAPI.queryUserDeparts(loginUser.getId()); + addition.put("departs", departs); + if (departs == null || departs.size() == 0) { + addition.put("multi_depart", 0); + } else if (departs.size() == 1) { + commonAPI.updateUserDepart(loginUser.getUsername(), departs.get(0).getOrgCode(),null); + addition.put("multi_depart", 1); + } else { + //查询当前是否有登录部门 + if(oConvertUtils.isEmpty(loginUser.getOrgCode())){ + commonAPI.updateUserDepart(loginUser.getUsername(), departs.get(0).getOrgCode(),null); + } + addition.put("multi_depart", 2); + } + + return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, addition); + } + + @Override + public boolean supports(Class authentication) { + return SocialGrantAuthenticationToken.class.isAssignableFrom(authentication); + } + + private static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) { + OAuth2ClientAuthenticationToken clientPrincipal = null; + if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) { + clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal(); + } + if (clientPrincipal != null && clientPrincipal.isAuthenticated()) { + return clientPrincipal; + } + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT); + } + + /** + * 登录失败超出次数5 返回true + * @param username + * @return + */ + private boolean isLoginFailOvertimes(String username){ + String key = CommonConstant.LOGIN_FAIL + username; + Object failTime = redisUtil.get(key); + if(failTime!=null){ + Integer val = Integer.parseInt(failTime.toString()); + if(val>5){ + return true; + } + } + return false; + } + + /** + * 记录登录失败次数 + * @param username + */ + private void addLoginFailOvertimes(String username){ + String key = CommonConstant.LOGIN_FAIL + username; + Object failTime = redisUtil.get(key); + Integer val = 0; + if(failTime!=null){ + val = Integer.parseInt(failTime.toString()); + } + // 10分钟 + redisUtil.set(key, ++val, 10); + } + + /** + * 校验用户是否有效 + */ + private void checkUserIsEffective(LoginUser loginUser) { + //情况1:根据用户信息查询,该用户不存在 + if (Objects.isNull(loginUser)) { + baseCommonService.addLog("用户登录失败,用户不存在!", CommonConstant.LOG_TYPE_1, null); + throw new JeecgBootException("该用户不存在,请注册"); + } + //情况2:根据用户信息查询,该用户已注销 + //update-begin---author:王帅 Date:20200601 for:if条件永远为falsebug------------ + if (CommonConstant.DEL_FLAG_1.equals(loginUser.getDelFlag())) { + //update-end---author:王帅 Date:20200601 for:if条件永远为falsebug------------ + baseCommonService.addLog("用户登录失败,用户名:" + loginUser.getUsername() + "已注销!", CommonConstant.LOG_TYPE_1, null); + throw new JeecgBootException("该用户已注销"); + } + //情况3:根据用户信息查询,该用户已冻结 + if (CommonConstant.USER_FREEZE.equals(loginUser.getStatus())) { + baseCommonService.addLog("用户登录失败,用户名:" + loginUser.getUsername() + "已冻结!", CommonConstant.LOG_TYPE_1, null); + throw new JeecgBootException("该用户已冻结"); + } + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationToken.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationToken.java new file mode 100644 index 000000000..455824d03 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/social/SocialGrantAuthenticationToken.java @@ -0,0 +1,20 @@ +package org.jeecg.config.security.social; + +import org.jeecg.config.security.LoginType; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken; + +import java.util.Map; + +/** + * @author EightMonth + * @date 2024/1/1 + */ +public class SocialGrantAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { + + public SocialGrantAuthenticationToken(Authentication clientPrincipal, Map additionalParameters) { + super(new AuthorizationGrantType(LoginType.SOCIAL), clientPrincipal, additionalParameters); + } + +} diff --git a/pom.xml b/pom.xml index 724ba787f..5e56b52a9 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,8 @@ 1.4.4 1.4.7 8.5.7 - 1.3.4 + 1.4.0 + 1.16.6 1.6.1 7.4.0 @@ -317,6 +318,12 @@ + + + me.zhyd.oauth + JustAuth + ${justauth.version} + com.squareup.okhttp3 okhttp