From baac7a6cc68b3a887d4428e686c9d75de70da36c Mon Sep 17 00:00:00 2001 From: ipixel Date: Thu, 4 Nov 2021 15:06:25 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E4=BB=A4=E7=89=8C=E8=8E=B7=E5=8F=96=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pigx/auth/endpoint/PigxTokenEndpoint.java | 12 ++++++++++++ .../service/PigxTokenDealServiceImpl.java | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java index 8422580d8..4e5a1b131 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/endpoint/PigxTokenEndpoint.java @@ -27,10 +27,12 @@ import com.pig4cloud.pigx.auth.service.PigxTokenDealServiceImpl; import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.security.annotation.Inner; +import com.pig4cloud.pigx.common.security.service.PigxUser; import com.pig4cloud.pigx.common.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.AuthorizationRequest; import org.springframework.security.oauth2.provider.ClientDetails; @@ -140,4 +142,14 @@ public class PigxTokenEndpoint { return dealService.queryToken(page); } + /** + * 根据访问令牌获取用户信息 + * @param accessToken 访问令牌 + * @return 登录用户信息 + */ + @GetMapping("/user") + public ResponseEntity userInfo(@RequestParam("access_token") String accessToken){ + return dealService.getUserInfo(accessToken); + } + } diff --git a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/service/PigxTokenDealServiceImpl.java b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/service/PigxTokenDealServiceImpl.java index 37d26e785..0ed0e6364 100644 --- a/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/service/PigxTokenDealServiceImpl.java +++ b/pigx-auth/src/main/java/com/pig4cloud/pigx/auth/service/PigxTokenDealServiceImpl.java @@ -9,6 +9,7 @@ import com.pig4cloud.pigx.common.core.constant.SecurityConstants; import com.pig4cloud.pigx.common.core.util.KeyStrResolver; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.core.util.SpringContextHolder; +import com.pig4cloud.pigx.common.security.service.PigxUser; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.CacheManager; @@ -18,9 +19,12 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.stereotype.Service; @@ -150,4 +154,19 @@ public class PigxTokenDealServiceImpl { return result; } + /** + * 根据访问令牌获取用户信息 + * @param accessToken 访问令牌 + * @return 登录用户信息 + */ + public ResponseEntity getUserInfo(String accessToken) { + OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken); + if (oAuth2AccessToken == null || StrUtil.isBlank(oAuth2AccessToken.getValue()) || oAuth2AccessToken.isExpired()) { + throw new InvalidRequestException("The access token is invalid or expired"); + } + OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(oAuth2AccessToken); + PigxUser userInfo = (PigxUser) auth2Authentication.getUserAuthentication().getPrincipal(); + return new ResponseEntity(userInfo, HttpStatus.OK); + } + } From 9568a56de0ebcf16306bc20c6e3e21bedbeab47d Mon Sep 17 00:00:00 2001 From: ipixel Date: Thu, 11 Nov 2021 16:28:38 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0QQ=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BF=AE=E6=94=B9=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E6=89=8B=E6=9C=BA=E5=8F=B7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/constant/SecurityConstants.java | 10 ++ .../pigx/admin/handler/SmsLoginHandler.java | 2 +- .../admin/handler/TencentLoginHandler.java | 121 ++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/TencentLoginHandler.java diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java index fe01e2683..26850f0e7 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/SecurityConstants.java @@ -105,6 +105,11 @@ public interface SecurityConstants { */ String MOBILE_TOKEN_URL = "/mobile/token/*"; + /** + * QQ获取token + */ + String QQ_AUTHORIZATION_CODE_URL = "https://graph.qq.com/oauth2.0/token?grant_type=" + + "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s"; /** * 微信获取OPENID */ @@ -128,6 +133,11 @@ public interface SecurityConstants { */ String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token"; + /** + * QQ获取用户信息 + */ + String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s"; + /** * 码云获取用户信息 */ diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java index 1e9fb5127..acb147f7b 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/SmsLoginHandler.java @@ -70,7 +70,7 @@ public class SmsLoginHandler extends AbstractLoginHandler { */ @Override public Boolean bind(SysUser user, String identify) { - user.setGiteeLogin(identify); + user.setPhone(identify); sysUserService.updateById(user); return null; } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/TencentLoginHandler.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/TencentLoginHandler.java new file mode 100644 index 000000000..ef227e30d --- /dev/null +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/handler/TencentLoginHandler.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2025, lengleng All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the pig4cloud.com developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: lengleng (wangiegie@gmail.com) + */ + +package com.pig4cloud.pigx.admin.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.pig4cloud.pigx.admin.api.dto.UserInfo; +import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails; +import com.pig4cloud.pigx.admin.api.entity.SysUser; +import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper; +import com.pig4cloud.pigx.admin.service.SysUserService; +import com.pig4cloud.pigx.common.core.constant.SecurityConstants; +import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author lengleng + * @date 2018/11/18 + */ +@Slf4j +@Component("QQ") +@AllArgsConstructor +public class TencentLoginHandler extends AbstractLoginHandler { + + private final SysUserService sysUserService; + + private final SysSocialDetailsMapper sysSocialDetailsMapper; + + /** + * QQ登录传入code + *

+ * 通过code 调用qq 获取唯一标识 + * @param code + * @return + */ + @Override + public String identify(String code) { + SysSocialDetails condition = new SysSocialDetails(); + condition.setType(LoginTypeEnum.QQ.getType()); + SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition)); + + String url = String.format(SecurityConstants.QQ_AUTHORIZATION_CODE_URL, socialDetails.getAppId(), + socialDetails.getAppSecret(), code); + String result = HttpUtil.get(url); + log.debug("QQ响应报文:{}", result); + + String accessToken = JSONUtil.parseObj(result).getStr("access_token"); + String userUrl = String.format(SecurityConstants.QQ_USER_INFO_URL, accessToken); + String resp = HttpUtil.get(userUrl); + log.debug("QQ获取个人信息返回报文{}", resp); + + JSONObject userInfo = JSONUtil.parseObj(resp); + // QQ唯一标识 + String openid = userInfo.getStr("openid"); + return openid; + } + + /** + * openId 获取用户信息 + * @param openId + * @return + */ + @Override + public UserInfo info(String openId) { + SysUser user = sysUserService.getOne(Wrappers.query().lambda().eq(SysUser::getQqOpenid, openId)); + + if (user == null) { + log.info("QQ未绑定:{}", openId); + return null; + } + return sysUserService.findUserInfo(user); + } + + /** + * 绑定逻辑 + * @param user 用户实体 + * @param identify 渠道返回唯一标识 + * @return + */ + @Override + public Boolean bind(SysUser user, String identify) { + List userList = sysUserService + .list(Wrappers.query().lambda().eq(SysUser::getQqOpenid, identify)); + + // 先把原有绑定关系去除,设置绑定为NULL + if (CollUtil.isNotEmpty(userList)) { + SysUser condition = new SysUser(); + condition.setQqOpenid(identify); + sysUserService.update(condition, Wrappers.lambdaUpdate().set(SysUser::getQqOpenid, null)); + log.info("QQ账号 {} 更换账号绑定", identify); + } + + user.setQqOpenid(identify); + sysUserService.updateById(user); + return null; + } + +} From cb7eb14a09bd7fa690601b210f89f04658397e75 Mon Sep 17 00:00:00 2001 From: ipixel Date: Thu, 11 Nov 2021 16:58:03 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= =?UTF-8?q?=EF=BC=8C=E9=98=B2=E6=AD=A2=E5=86=85=E5=AE=B9=E4=B8=AD=E5=90=AB?= =?UTF-8?q?=E6=9C=89=E5=A4=9A=E4=BD=99=E7=9A=84@=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pigx/admin/service/impl/SysSocialDetailsServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysSocialDetailsServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysSocialDetailsServiceImpl.java index 825fc65d2..5b6dccd76 100755 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysSocialDetailsServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysSocialDetailsServiceImpl.java @@ -79,7 +79,7 @@ public class SysSocialDetailsServiceImpl extends ServiceImpl Date: Mon, 14 Feb 2022 15:27:24 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=90=8C=E6=97=B6=E5=9C=A8=E7=BA=BF,=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E6=95=B0=E9=87=8F=E5=8F=AF=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=8F=AA=E8=83=BD?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E4=B8=80=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/constant/CommonConstants.java | 6 ++ .../service/PigxCustomTokenServices.java | 61 ++++++++++++------- .../api/dto/SysOauthClientDetailsDTO.java | 5 ++ .../SysOauthClientDetailsServiceImpl.java | 8 ++- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java index a20223926..77164f3db 100644 --- a/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java +++ b/pigx-common/pigx-common-core/src/main/java/com/pig4cloud/pigx/common/core/constant/CommonConstants.java @@ -110,4 +110,10 @@ public interface CommonConstants { */ String ENC_FLAG = "enc_flag"; + + /** + * 客户端允许同时在线数量 + */ + String ONLINE_QUANTITY = "online_quantity"; + } diff --git a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxCustomTokenServices.java b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxCustomTokenServices.java index 1d5ef53d4..0e0dc6861 100644 --- a/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxCustomTokenServices.java +++ b/pigx-common/pigx-common-security/src/main/java/com/pig4cloud/pigx/common/security/service/PigxCustomTokenServices.java @@ -1,5 +1,9 @@ package com.pig4cloud.pigx.common.security.service; +import cn.hutool.core.map.MapUtil; +import com.pig4cloud.pigx.common.core.constant.CommonConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; @@ -14,9 +18,7 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; -import java.util.Date; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * 自定义 token 放发处理逻辑 @@ -27,10 +29,14 @@ import java.util.UUID; public class PigxCustomTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean { + protected final Log logger = LogFactory.getLog(getClass()); + private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days. private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours. + private int defaultOnlineQuantity = 1; //客户端默认同时在线数量 + private boolean supportRefreshToken = false; private boolean reuseRefreshToken = true; @@ -54,28 +60,25 @@ public class PigxCustomTokenServices implements AuthorizationServerTokenServices @Override public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); - OAuth2RefreshToken refreshToken = null; - // 若已产生token , 过期时删除相关token,执行下边的重新生成逻辑 if (existingAccessToken != null) { - tokenStore.removeAccessToken(existingAccessToken); - if (existingAccessToken.getRefreshToken() != null) { - refreshToken = existingAccessToken.getRefreshToken(); - tokenStore.removeRefreshToken(refreshToken); - } - } - - if (refreshToken == null) { - refreshToken = createRefreshToken(authentication); - } - - else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { - ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; - if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { - refreshToken = createRefreshToken(authentication); + //增加允许同时在线数量逻辑 + Collection currentTokenList = tokenStore.findTokensByClientIdAndUserName(authentication.getOAuth2Request().getClientId(), authentication.getName()); + if (currentTokenList.size() >= getOnlineQuantity(authentication.getOAuth2Request())) { + Optional oldestTokenOpt = currentTokenList.stream().min(Comparator.comparing(OAuth2AccessToken::getExpiration)); + if (oldestTokenOpt.isPresent()) { + OAuth2AccessToken oldestToken = oldestTokenOpt.get(); + logger.info("online quantity reaches the upper limit"); + tokenStore.removeAccessToken(oldestToken); + if (oldestToken.getRefreshToken() != null) { + OAuth2RefreshToken refreshToken = oldestToken.getRefreshToken(); + tokenStore.removeRefreshToken(refreshToken); + } + } } } + OAuth2RefreshToken refreshToken = createRefreshToken(authentication); OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); @@ -270,7 +273,7 @@ public class PigxCustomTokenServices implements AuthorizationServerTokenServices if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); Integer validity = client.getAccessTokenValiditySeconds(); - if (validity != null) { + if (validity != null && validity > 0) { return validity; } } @@ -286,13 +289,27 @@ public class PigxCustomTokenServices implements AuthorizationServerTokenServices if (clientDetailsService != null) { ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); Integer validity = client.getRefreshTokenValiditySeconds(); - if (validity != null) { + if (validity != null && validity > 0) { return validity; } } return refreshTokenValiditySeconds; } + /** + * the client online quantity + * + * @param clientAuth the current authorization request + * @return the client online quantity + */ + protected int getOnlineQuantity(OAuth2Request clientAuth) { + if (clientDetailsService != null) { + ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + return MapUtil.getInt(client.getAdditionalInformation(), CommonConstants.ONLINE_QUANTITY, 1); + } + return defaultOnlineQuantity; + } + /** * Is a refresh token supported for this client (or the global setting if * {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not diff --git a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/SysOauthClientDetailsDTO.java b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/SysOauthClientDetailsDTO.java index dcc3e5a4d..7638db5b9 100644 --- a/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/SysOauthClientDetailsDTO.java +++ b/pigx-upms/pigx-upms-api/src/main/java/com/pig4cloud/pigx/admin/api/dto/SysOauthClientDetailsDTO.java @@ -22,4 +22,9 @@ public class SysOauthClientDetailsDTO extends SysOauthClientDetails { */ private String encFlag; + /** + * 客户端允许同时在线数量 + */ + private String onlineQuantity; + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysOauthClientDetailsServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysOauthClientDetailsServiceImpl.java index 0cd065123..1631c3cc4 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysOauthClientDetailsServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysOauthClientDetailsServiceImpl.java @@ -89,7 +89,8 @@ public class SysOauthClientDetailsServiceImpl extends ServiceImpl