后台登录微信二次认证
This commit is contained in:
1 parent
10d6900777
commit
39878e0aa5
21 files changed
+701
-5
No files matched your search
+120
@@ -0,0 +1,120 @@
|
||||
package com.bsd.pjcloud.auth.support;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.bsd.pjcloud.admin.api.dto.UserInfo;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
|
||||
import com.bsd.pjcloud.common.core.constant.CacheConstants;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.core.util.RetOps;
|
||||
import com.bsd.pjcloud.common.data.resolver.ParamResolver;
|
||||
import com.bsd.pjcloud.common.security.exception.WechatBindRequiredException;
|
||||
import com.bsd.pjcloud.common.security.exception.WechatConfirmRequiredException;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* PC 后台(pig 客户端)密码登录:可选绑定 / 扫码确认 PC 微信
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PcWechatPasswordLoginGate {
|
||||
|
||||
private static final String PC_PASSWORD_CLIENT_ID = "pig";
|
||||
|
||||
private static final String PARAM_BIND_REQUIRED = "PC_WECHAT_BIND_REQUIRED";
|
||||
|
||||
private static final String PARAM_CONFIRM_REQUIRED = "PC_WECHAT_CONFIRM_REQUIRED";
|
||||
|
||||
private static final long TICKET_TTL_MINUTES = 15;
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
private final PcWechatSecurityProperties pcWechatSecurityProperties;
|
||||
|
||||
public void check(RegisteredClient registeredClient, Authentication authentication) {
|
||||
if (registeredClient == null || !PC_PASSWORD_CLIENT_ID.equals(registeredClient.getClientId())) {
|
||||
return;
|
||||
}
|
||||
if (!isBindRequired() && !isConfirmRequired()) {
|
||||
return;
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (!(principal instanceof PjcloudUser pjcloudUser)) {
|
||||
return;
|
||||
}
|
||||
R<UserInfo> userResult = remoteUserService.getInnerByUserId(pjcloudUser.getId());
|
||||
UserInfo userInfo = RetOps.of(userResult).getData().orElse(null);
|
||||
if (userInfo == null || userInfo.getSysUser() == null) {
|
||||
return;
|
||||
}
|
||||
String wxPcOpenid = userInfo.getSysUser().getWxPcOpenid();
|
||||
String ticket = IdUtil.simpleUUID();
|
||||
if (StrUtil.isBlank(wxPcOpenid)) {
|
||||
if (!isBindRequired() && !isConfirmRequired()) {
|
||||
return;
|
||||
}
|
||||
// 未绑定:只要开启了绑定或扫码确认,均须先扫码绑定
|
||||
redisTemplate.opsForValue().set(pwdVerifiedKey(ticket), String.valueOf(pjcloudUser.getId()), TICKET_TTL_MINUTES,
|
||||
TimeUnit.MINUTES);
|
||||
throw new WechatBindRequiredException(ticket);
|
||||
}
|
||||
if (!isConfirmRequired()) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(loginConfirmKey(ticket), String.valueOf(pjcloudUser.getId()), TICKET_TTL_MINUTES,
|
||||
TimeUnit.MINUTES);
|
||||
throw new WechatConfirmRequiredException(ticket);
|
||||
}
|
||||
|
||||
private boolean isBindRequired() {
|
||||
return resolveFlag(PARAM_BIND_REQUIRED, pcWechatSecurityProperties.isBindRequired());
|
||||
}
|
||||
|
||||
private boolean isConfirmRequired() {
|
||||
return resolveFlag(PARAM_CONFIRM_REQUIRED, pcWechatSecurityProperties.isConfirmRequired());
|
||||
}
|
||||
|
||||
private boolean resolveFlag(String paramKey, boolean defaultValue) {
|
||||
try {
|
||||
String value = ParamResolver.getStr(paramKey);
|
||||
if (StrUtil.isNotBlank(value)) {
|
||||
return parseBool(value);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.debug("读取公共参数 {} 失败,使用配置文件默认值", paramKey);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private boolean parseBool(String value) {
|
||||
if ("1".equals(value) || "Y".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value)) {
|
||||
return true;
|
||||
}
|
||||
if ("0".equals(value) || "N".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value)) {
|
||||
return false;
|
||||
}
|
||||
return BooleanUtil.toBoolean(value);
|
||||
}
|
||||
|
||||
private String pwdVerifiedKey(String ticket) {
|
||||
return CacheConstants.PC_PWD_VERIFIED + ticket;
|
||||
}
|
||||
|
||||
private String loginConfirmKey(String ticket) {
|
||||
return CacheConstants.PC_LOGIN_CONFIRM + ticket;
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.bsd.pjcloud.auth.support;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* PC 后台密码登录微信校验(开发/生产可关)
|
||||
* <p>
|
||||
* 配置示例(pjcloud-auth application.yml 或 Nacos):
|
||||
* <pre>
|
||||
* security:
|
||||
* pc-wechat:
|
||||
* bind-required: false
|
||||
* confirm-required: false
|
||||
* </pre>
|
||||
* 也可在「参数管理」覆盖:PC_WECHAT_BIND_REQUIRED、PC_WECHAT_CONFIRM_REQUIRED(true/false 或 1/0)
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@RefreshScope
|
||||
@ConfigurationProperties("security.pc-wechat")
|
||||
public class PcWechatSecurityProperties {
|
||||
|
||||
/**
|
||||
* 密码登录后未绑定 PC 微信时是否拦截(须先扫码绑定)
|
||||
*/
|
||||
private boolean bindRequired = false;
|
||||
|
||||
/**
|
||||
* 已绑定 PC 微信时,密码登录是否须微信扫码确认
|
||||
*/
|
||||
private boolean confirmRequired = false;
|
||||
|
||||
}
|
||||
+15
@@ -2,7 +2,10 @@ package com.bsd.pjcloud.auth.support.base;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.bsd.pjcloud.auth.support.PcWechatPasswordLoginGate;
|
||||
import com.bsd.pjcloud.common.core.constant.CommonConstants;
|
||||
import com.bsd.pjcloud.common.security.exception.WechatBindRequiredException;
|
||||
import com.bsd.pjcloud.common.security.exception.WechatConfirmRequiredException;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudRedisOAuth2AuthorizationService;
|
||||
import com.bsd.pjcloud.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.bsd.pjcloud.common.security.util.ScopeException;
|
||||
@@ -148,6 +151,10 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
if (AuthorizationGrantType.PASSWORD.equals(resouceOwnerBaseAuthentication.getAuthorizationGrantType())) {
|
||||
SpringUtil.getBean(PcWechatPasswordLoginGate.class).check(registeredClient, usernamePasswordAuthentication);
|
||||
}
|
||||
|
||||
Object onlineQuantity = registeredClient.getClientSettings()
|
||||
.getSettings()
|
||||
.get(CommonConstants.ONLINE_QUANTITY);
|
||||
@@ -303,6 +310,14 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
||||
"User credentials have expired"),
|
||||
""));
|
||||
}
|
||||
if (authenticationException instanceof WechatBindRequiredException bindEx) {
|
||||
return new OAuth2AuthenticationException(
|
||||
new OAuth2Error(OAuth2ErrorCodesExpand.WECHAT_BIND_REQUIRED, bindEx.getTicket(), ""));
|
||||
}
|
||||
if (authenticationException instanceof WechatConfirmRequiredException confirmEx) {
|
||||
return new OAuth2AuthenticationException(
|
||||
new OAuth2Error(OAuth2ErrorCodesExpand.WECHAT_CONFIRM_REQUIRED, confirmEx.getTicket(), ""));
|
||||
}
|
||||
if (authenticationException instanceof ScopeException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "invalid_scope"), ""));
|
||||
|
||||
Reference in new issue
Block a user