车间能级提升-智能设备管理系统
zhuguifei
2025-04-08 3baaad59171ded6aca17340fcc907acbeeb45b7b
Merge branch 'main' of http://lanpucloud.cn:1111/r/eims-master
已添加2个文件
已修改11个文件
304 ■■■■■ 文件已修改
eims-ui/apps/web-antd/src/views/_core/authentication/login.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/_core/oauth-common.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/application-dev.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-admin/src/main/resources/application-prod.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/_core/authentication/login.vue
@@ -8,7 +8,7 @@
import { omit } from 'lodash-es';
import { tenantList, type TenantResp } from '#/api';
import { authBinding, tenantList, type TenantResp } from '#/api';
import { captchaImage, type CaptchaResponse } from '#/api/core/captcha';
import { useAuthStore } from '#/store';
@@ -50,7 +50,11 @@
}
onMounted(async () => {
  // å¯åŠ¨å•ç‚¹ç™»å½•æ³¨é‡ŠæŽ‰ä¸‹è¾¹è¿™ä¸€è¡Œï¼Œå¦åˆ™æ”¾å¼€
  await Promise.all([loadCaptcha(), loadTenant()]);
  // å¯åŠ¨å•ç‚¹ç™»å½•æ”¾å¼€ä¸‹è¾¹ä¸¤è¡Œæ³¨é‡Šï¼Œå¦åˆ™æ³¨é‡ŠæŽ‰
  // const href = await authBinding('keycloak', '000000');
  // window.location.href = href;
});
const formSchema = computed((): VbenFormSchema[] => {
eims-ui/apps/web-antd/src/views/_core/oauth-common.ts
@@ -59,6 +59,7 @@
 * action不为空的会在登录页显示
 */
export const accountBindList: BindItem[] = [
  {
    avatar: TaobaoIcon,
    color: '#ff4000',
@@ -101,4 +102,13 @@
    source: 'github',
    title: 'GITHUB',
  },
  {
    action: () => handleAuthBinding('keycloak'),
    avatar: TaobaoIcon,
    color: '#ff4000',
    description: 'keycloak登录',
    key: '6',
    source: 'keycloak',
    title: 'keycloak',
  },
];
eims-ui/apps/web-antd/src/views/_core/social-callback/index.vue
@@ -18,7 +18,7 @@
const state = route.query.state as string;
const stateJson = JSON.parse(atob(state));
// æ¥æº
const source = route.query.source as string;
const source = 'keycloak';
// ç§Ÿæˆ·ID
const defaultTenantId = '000000';
const tenantId = (stateJson.tenantId as string) ?? defaultTenantId;
eims/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
@@ -6,6 +6,8 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.baomidou.lock.annotation.Lock4j;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -48,6 +50,12 @@
@Slf4j
@Service
public class SysLoginService {
    @Value("${justauth.type.keycloak.server-url}")
    private String keycloakServerUrl;
    @Value("${justauth.type.keycloak.realm}")
    private String keycloakRealm;
    @Value("${user.password.maxRetryCount}")
    private Integer maxRetryCount;
@@ -116,6 +124,27 @@
                TenantHelper.clearDynamic();
            }
            recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
            // æ–°å¢žKeycloak登出逻辑
            Long userId = loginUser.getUserId();
            SysSocialVo social = sysSocialService.selectByUserId(userId);
            if (social == null) {
                return;
            }
            String logoutUrl = keycloakServerUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/logout";
            HttpRequest request = HttpRequest.get(logoutUrl)
                .form("refresh_token", social.getRefreshToken())
                .form("id_token_hint", social.getIdToken());
            HttpResponse response = request.execute();
            if (response.isOk()) {
                System.out.println("1234");
            }
        } catch (NotLoginException ignored) {
        } finally {
            try {
eims/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
@@ -7,6 +7,7 @@
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.aizuda.snailjob.common.core.constant.SystemConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
@@ -23,6 +24,8 @@
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysSocialBo;
import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysSocialVo;
import org.dromara.system.domain.vo.SysUserVo;
@@ -77,6 +80,47 @@
                    .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
                    .executeAsync();
        }
        if ("KEYCLOAK".equals(authUserData.getSource())) {
            // æ–°å¢žKEYCLOAK用户自动创建逻辑
            String authId = authUserData.getSource() + authUserData.getUuid();
            List<SysSocialVo> list = sysSocialService.selectByAuthId(authId);
            if (CollUtil.isEmpty(list)) {
                // è‡ªåŠ¨åˆ›å»ºæ–°ç”¨æˆ·
                SysUser newUser = new SysUser();
                newUser.setUserName(authUserData.getUsername());
                newUser.setEmail(authUserData.getEmail());
                newUser.setNickName(authUserData.getNickname());
                newUser.setPassword("Initial123@"); // åˆå§‹å¯†ç éœ€ç¬¦åˆå®‰å…¨ç­–ç•¥
                newUser.setStatus("0");
                userMapper.insert(newUser); // å‡è®¾å­˜åœ¨æ’入方法
                // åˆ›å»ºç¤¾äº¤ç»‘定记录
                SysSocialBo newSocial = new SysSocialBo();
                newSocial.setUserId(newUser.getUserId());
                newSocial.setUserName(newUser.getUserName());
                newSocial.setAuthId(authId);
                newSocial.setSource(authUserData.getSource());
                newSocial.setTenantId(newUser.getTenantId());
                newSocial.setOpenId(authUserData.getUuid());
                newSocial.setAccessToken(authUserData.getToken().getAccessToken());
                newSocial.setRefreshToken(authUserData.getToken().getRefreshToken());
                newSocial.setIdToken(authUserData.getToken().getIdToken());
                sysSocialService.insertByBo(newSocial); // éœ€ç¡®ä¿æœåŠ¡æœ‰æ–°å¢žæ–¹æ³•
                // é‡æ–°æŸ¥è¯¢ç¡®ä¿æ•°æ®å¯ç”¨
                list = sysSocialService.selectByAuthId(authId);
            } else {
                // æ›´æ–°ç¤¾äº¤ç»‘定记录
                SysSocialBo socialBo = new SysSocialBo();
                socialBo.setId(list.get(0).getId());
                socialBo.setAccessToken(authUserData.getToken().getAccessToken());
                socialBo.setRefreshToken(authUserData.getToken().getRefreshToken());
                socialBo.setIdToken(authUserData.getToken().getIdToken());
                sysSocialService.updateByBo(socialBo);
            }
        }
        List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
        if (CollUtil.isEmpty(list)) {
eims/ruoyi-admin/src/main/resources/application-dev.yml
@@ -187,8 +187,16 @@
--- # ä¸‰æ–¹æŽˆæƒ
justauth:
  # å‰ç«¯å¤–网访问地址
  address: http://localhost:80
  address: http://192.168.12.236:80
  type:
    keycloak:
      # keycloak æœåŠ¡å™¨åœ°å€
      server-url: https://lanbaosystem.shlanbao.cn:8443
      realm: lanbao
      client-id: DataCapture
      client-secret: kplisa4lJHEIM6knqefVbxln85QbA5NX
      redirect-uri: ${justauth.address}/social-callback
      scopes: [ openid, email, phone, profile ]
    maxkey:
      # maxkey æœåŠ¡å™¨åœ°å€
      # æ³¨æ„ å¦‚下均配置均不需要修改 maxkey å·²ç»å†…置好了数据
eims/ruoyi-admin/src/main/resources/application-prod.yml
@@ -189,8 +189,16 @@
--- # ä¸‰æ–¹æŽˆæƒ
justauth:
  # å‰ç«¯å¤–网访问地址
  address: http://localhost:80
  address: http://192.168.0.24:80
  type:
    keycloak:
      # keycloak æœåŠ¡å™¨åœ°å€
      server-url: https://lanbaosystem.shlanbao.cn:8443
      realm: lanbao
      client-id: DataCapture
      client-secret: kplisa4lJHEIM6knqefVbxln85QbA5NX
      redirect-uri: ${justauth.address}/social-callback
      scopes: [ openid, email, phone, profile ]
    maxkey:
      # maxkey æœåŠ¡å™¨åœ°å€
      # æ³¨æ„ å¦‚下均配置均不需要修改 maxkey å·²ç»å†…置好了数据
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
@@ -72,4 +72,7 @@
     */
    private List<String> scopes;
    private String realm;
}
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakRequest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
package org.dromara.common.social.keycloak;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.xkcoding.http.support.HttpHeader;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import org.dromara.common.core.domain.model.PasswordLoginBody;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
 * Keycloak OAuth2 è®¤è¯è¯·æ±‚
 */
public class AuthKeycloakRequest extends AuthDefaultRequest {
    public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.keycloak.server-url");
    public static final String REALM = SpringUtils.getProperty("justauth.type.keycloak.realm");
    public AuthKeycloakRequest(AuthConfig config) {
        super(config, AuthKeycloakSource.KEYCLOAK);
    }
    public AuthKeycloakRequest(AuthConfig config, AuthStateCache authStateCache) {
        super(config, AuthKeycloakSource.KEYCLOAK, authStateCache);
    }
    public  AuthToken getAccessToken(PasswordLoginBody loginBody) {
        HttpRequest request = HttpRequest.post(SERVER_URL + "/realms/" + REALM + "/protocol/openid-connect/token")
            .form("grant_type", "password")
            .form("client_id", config.getClientId())
            .form("client_secret", config.getClientSecret())
            .form("username", loginBody.getUsername())
            .form("password", loginBody.getPassword())
            .form("scope", "openid");
        HttpResponse response = request.execute();
        Dict object = JsonUtils.parseMap(response.body());
        if (object.containsKey("error")) {
            throw new AuthException(object.getStr("error_description"));
        }
        if (object.containsKey("message")) {
            throw new AuthException(object.getStr("message"));
        }
        return AuthToken.builder()
            .accessToken(object.getStr("access_token"))
            .refreshToken(object.getStr("refresh_token"))
            .idToken(object.getStr("id_token"))
            .tokenType(object.getStr("token_type"))
            .expireIn(object.getInt("expires_in"))
            .build();
    }
    @Override
    public AuthToken getAccessToken(AuthCallback authCallback) {
        String body = doPostAuthorizationCode(authCallback.getCode());
        Dict object = JsonUtils.parseMap(body);
        if (object.containsKey("error")) {
            throw new AuthException(object.getStr("error_description"));
        }
        if (object.containsKey("message")) {
            throw new AuthException(object.getStr("message"));
        }
        return AuthToken.builder()
            .accessToken(object.getStr("access_token"))
            .refreshToken(object.getStr("refresh_token"))
            .idToken(object.getStr("id_token"))
            .tokenType(object.getStr("token_type"))
            .expireIn(object.getInt("expires_in"))
            .build();
    }
    @Override
    public AuthUser getUserInfo(AuthToken authToken) {
        String body = doGetUserInfo(authToken);
        Dict object = JsonUtils.parseMap(body);
        if (object.containsKey("error")) {
            throw new AuthException(object.getStr("error_description"));
        }
        if (object.containsKey("message")) {
            throw new AuthException(object.getStr("message"));
        }
        return AuthUser.builder()
            .uuid(object.getStr("sub"))
            .username(object.getStr("preferred_username"))
            .nickname(object.getStr("name"))
            .email(object.getStr("email"))
            .token(authToken)
            .source(this.source.toString())
            .build();
    }
    @Override
    protected String doPostAuthorizationCode(String code) {
        HttpRequest request = HttpRequest.post(source.accessToken())
            .header("Authorization", "Basic " + Base64.encode("%s:%s".formatted(config.getClientId(), config.getClientSecret())))
            .form("grant_type", "authorization_code")
            .form("code", code)
            .form("redirect_uri", config.getRedirectUri());
        HttpResponse response = request.execute();
        return response.body();
    }
    @Override
    protected String doGetUserInfo(AuthToken authToken) {
        try {
            return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
                .add("Content-Type", "application/json")
                .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public String authorize(String state) {
        return UrlBuilder.fromBaseUrl(super.authorize(state))
            .queryParam("scope", StrUtil.join("%20", config.getScopes()))
            .build();
    }
}
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/keycloak/AuthKeycloakSource.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package org.dromara.common.social.keycloak;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthDefaultRequest;
public enum AuthKeycloakSource implements AuthSource {
    KEYCLOAK {
        /**
         * æŽˆæƒçš„api
         */
        @Override
        public String authorize() {
            return String.format("%s/realms/%s/protocol/openid-connect/auth", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
        }
        @Override
        public String accessToken() {
            return String.format("%s/realms/%s/protocol/openid-connect/token", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
        }
        @Override
        public String userInfo() {
            return String.format("%s/realms/%s/protocol/openid-connect/userinfo", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
        }
        public String logout() {
            return String.format("%s/realms/%s/protocol/openid-connect/logout", AuthKeycloakRequest.SERVER_URL, AuthKeycloakRequest.REALM);
        }
        @Override
        public Class<? extends AuthDefaultRequest> getTargetClass() {
            return AuthKeycloakRequest.class;
        }
    }
}
eims/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
@@ -10,6 +10,7 @@
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.keycloak.AuthKeycloakRequest;
import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
import org.dromara.common.social.topiam.AuthTopIamRequest;
@@ -66,6 +67,7 @@
            case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
            case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
            case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
            case "keycloak" -> new AuthKeycloakRequest(builder.build(), STATE_CACHE);
            default -> throw new AuthException("未获取到有效的Auth配置");
        };
    }
eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
@@ -49,5 +49,6 @@
     */
    List<SysSocialVo> selectByAuthId(String authId);
    SysSocialVo selectByUserId(Long userId);
}
eims/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
@@ -109,4 +109,11 @@
        return baseMapper.selectVoList(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getAuthId, authId));
    }
    @Override
    public SysSocialVo selectByUserId(Long userId) {
        SysSocialVo socialVo = baseMapper.selectVoOne(new LambdaQueryWrapper<SysSocial>().eq(SysSocial::getUserId, userId));
        return socialVo;
    }
}