From a57a16f678c31d154eaa0c95a083b7f8b1af312d Mon Sep 17 00:00:00 2001 From: liuhuazhong Date: Tue, 5 Nov 2024 14:35:17 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/config/Oauth2ClientProperties.java | 25 ++++ .../auth/controller/TokenController.java | 79 +++++++++++- .../com/ruoyi/auth/form/AccessTokenVo.java | 119 ++++++++++++++++++ .../ruoyi/auth/service/SysLoginService.java | 59 +++++++++ ruoyi-auth/src/main/resources/bootstrap.yml | 22 +++- .../src/main/resources/bootstrap.yml | 12 +- .../src/main/resources/bootstrap.yml | 6 +- 7 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 ruoyi-auth/src/main/java/com/ruoyi/auth/config/Oauth2ClientProperties.java create mode 100644 ruoyi-auth/src/main/java/com/ruoyi/auth/form/AccessTokenVo.java diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/config/Oauth2ClientProperties.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/Oauth2ClientProperties.java new file mode 100644 index 00000000..848f2fee --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/config/Oauth2ClientProperties.java @@ -0,0 +1,25 @@ +package com.ruoyi.auth.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "oauth2") +@Getter +@Setter +public class Oauth2ClientProperties { + + private String clientId; + private String clientSecret; + private String scope; + private String userAuthorizationUri; + private String accessTokenUri; + private String redirectUri; + private String logoutUri; + private String checkTokenUri; + private String loginPage; +} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java index c53e91a3..4681a385 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/TokenController.java @@ -2,12 +2,19 @@ package com.ruoyi.auth.controller; import javax.servlet.http.HttpServletRequest; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.auth.config.Oauth2ClientProperties; +import com.ruoyi.auth.form.AccessTokenVo; import com.ruoyi.auth.form.LoginKeyBody; +import com.ruoyi.common.core.exception.ServiceException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.*; import com.ruoyi.auth.form.LoginBody; import com.ruoyi.auth.form.RegisterBody; import com.ruoyi.auth.service.SysLoginService; @@ -18,6 +25,9 @@ import com.ruoyi.common.security.auth.AuthUtil; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.system.api.model.LoginUser; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; /** * token 控制 @@ -33,6 +43,9 @@ public class TokenController @Autowired private SysLoginService sysLoginService; + @Autowired + private Oauth2ClientProperties oauth2ClientProperties; + @PostMapping("login") public R login(@RequestBody LoginBody form) { @@ -51,6 +64,64 @@ public class TokenController return R.ok(tokenService.createToken(userInfo)); } + @PostMapping("loginByOauth2") + public R loginByOauth2(@RequestBody Map params) + /** + * { + * "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZGVtby1hcHAiXSwiZXhwIjoxNzI5MTU0MTExLCJ1c2VyX25hbWUiOiJhZG1pbiIsImp0aSI6IjQzNGVkNmI0LWIzN2MtNDliMS04NTczLWZhNmU4YTg5YTUxYSIsImNsaWVudF9pZCI6IkFCQyIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.U591q4fUaUBtBt5Ex-S2daM7DIl9-Ov0MsveymNfHxI", + * "token_type": "bearer", + * "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZGVtby1hcHAiXSwidXNlcl9uYW1lIjoiYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiNDM0ZWQ2YjQtYjM3Yy00OWIxLTg1NzMtZmE2ZThhODlhNTFhIiwiZXhwIjoxNzMxNjQ5OTY4LCJqdGkiOiJiODFhNmJkMC02ZDQzLTQ4M2QtOThmMy1hZWIxNDcyZDUwNzciLCJjbGllbnRfaWQiOiJBQkMifQ.0uMYVqW1G7j0chwxIdWGwpjDr12ogZPcD1iQfPsAs5k", + * "expires_in": 7198, + * "scope": "read write", + * "account_info": { + * "id": 1, + * "clientId": "ABC", + * "username": "admin", + * "mobile": "1232378743", + * "email": "abc@123.com" + * } + * } + */ + { + if (params.containsKey("code") && StringUtils.isNotBlank(params.get("code"))){ + RestTemplate restTemplate = new RestTemplate(); + String url = oauth2ClientProperties.getAccessTokenUri(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map= new LinkedMultiValueMap<>(); + map.add("grant_type", "authorization_code"); + map.add("code", params.get("code")); + map.add("redirect_uri", oauth2ClientProperties.getRedirectUri()); + map.add("client_id", oauth2ClientProperties.getClientId()); + map.add("client_secret", oauth2ClientProperties.getClientSecret()); + + HttpEntity> request = new HttpEntity<>(map, headers); + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + String body = response.getBody(); + + AccessTokenVo accessTokenVo = JSON.parseObject(body, AccessTokenVo.class); + String accessToken = accessTokenVo.getAccess_token(); + AccessTokenVo.Account_info accountInfo = accessTokenVo.getAccount_info(); + + // 用户登录 + LoginUser userInfo = sysLoginService.login(accountInfo); + // 获取登录token + Map token = tokenService.createToken(userInfo); + token.put("checkTokenUri",oauth2ClientProperties.getCheckTokenUri()); + token.put("logoutUri",oauth2ClientProperties.getLogoutUri()); + token.put("oauth2AccessToken",accessToken); + return R.ok(token); + } + + throw new ServiceException("用户信息获取失败"); + } + + + @GetMapping("oauth2ClientInfo") + public R getClientInfo() { + return R.ok(JSON.toJSON(oauth2ClientProperties)); + } + @DeleteMapping("logout") public R logout(HttpServletRequest request) { diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/form/AccessTokenVo.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/AccessTokenVo.java new file mode 100644 index 00000000..2d47e1fc --- /dev/null +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/form/AccessTokenVo.java @@ -0,0 +1,119 @@ +package com.ruoyi.auth.form; + +import java.io.Serializable; +import java.lang.Integer; +import java.lang.String; + +public class AccessTokenVo implements Serializable { + private String access_token; + + private String refresh_token; + + private String scope; + + private Account_info account_info; + + private String token_type; + + private Integer expires_in; + + public String getAccess_token() { + return this.access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public String getRefresh_token() { + return this.refresh_token; + } + + public void setRefresh_token(String refresh_token) { + this.refresh_token = refresh_token; + } + + public String getScope() { + return this.scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public Account_info getAccount_info() { + return this.account_info; + } + + public void setAccount_info(Account_info account_info) { + this.account_info = account_info; + } + + public String getToken_type() { + return this.token_type; + } + + public void setToken_type(String token_type) { + this.token_type = token_type; + } + + public Integer getExpires_in() { + return this.expires_in; + } + + public void setExpires_in(Integer expires_in) { + this.expires_in = expires_in; + } + + public static class Account_info implements Serializable { + private String clientId; + + private String mobile; + + private Integer id; + + private String email; + + private String username; + + public String getClientId() { + return this.clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getMobile() { + return this.mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getId() { + return this.id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + } +} diff --git a/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java index 1bf324a3..2e1a57e8 100644 --- a/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java +++ b/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java @@ -1,5 +1,6 @@ package com.ruoyi.auth.service; +import com.ruoyi.auth.form.AccessTokenVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.ruoyi.common.core.constant.CacheConstants; @@ -193,4 +194,62 @@ public class SysLoginService } return userInfo; } + + public LoginUser login(AccessTokenVo.Account_info accountInfo) { + + String username = accountInfo.getUsername(); + + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username)) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); + throw new ServiceException("用户/密码必须填写"); + } + + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new ServiceException("用户名不在指定范围"); + } + // IP黑名单校验 + String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); + throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); + } + // 查询用户信息 + R userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + + if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new ServiceException("登录用户:" + username + " 不存在"); + // register(username, "123456"); +// userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + } + + if (R.FAIL == userResult.getCode()) + { + throw new ServiceException(userResult.getMsg()); + } + + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + + recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + return userInfo; + } } diff --git a/ruoyi-auth/src/main/resources/bootstrap.yml b/ruoyi-auth/src/main/resources/bootstrap.yml index 95a9dfdd..625b9b8f 100644 --- a/ruoyi-auth/src/main/resources/bootstrap.yml +++ b/ruoyi-auth/src/main/resources/bootstrap.yml @@ -13,17 +13,19 @@ spring: cloud: nacos: discovery: + namespace: cc149503-5508-4da9-a735-9505e2a91363 # 服务注册地址 - server-addr: nacos-ci4s.argo.svc:8848 + server-addr: 172.20.32.181:31203 username: nacos password: h1n2x3j4y5@ retry: enabled: true config: + namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: nacos-ci4s.argo.svc:8848 + server-addr: 172.20.32.181:31203 # 配置文件格式 file-extension: yml # 共享配置 @@ -36,6 +38,18 @@ spring: refresh: true redis: - host: 172.20.32.150 + host: 172.20.32.88 port: 6379 - password: \ No newline at end of file + password: + +oauth2: + url: http://172.20.32.106:8080 +# url: http://110.41.62.43:8088 + client-id: ci4s + client-secret: 123456 + user-authorization-uri: ${oauth2.url}/oauth/authorize + access-token-uri: ${oauth2.url}/oauth/token + logout-uri: ${oauth2.url}/logout + redirect-uri: http://172.20.32.83:8002/authorize + check-token-uri: ${oauth2.url}/oauth/check_token + login-page: ${oauth2.url}/login.html diff --git a/ruoyi-gateway/src/main/resources/bootstrap.yml b/ruoyi-gateway/src/main/resources/bootstrap.yml index d389a43a..7f060cdc 100644 --- a/ruoyi-gateway/src/main/resources/bootstrap.yml +++ b/ruoyi-gateway/src/main/resources/bootstrap.yml @@ -3,7 +3,11 @@ server: port: 8082 # Spring -spring: +spring: + redis: + host: 172.20.32.88 + port: 6379 + password: application: # 应用名称 name: ruoyi-gateway @@ -14,16 +18,18 @@ spring: nacos: discovery: # 服务注册地址 - server-addr: nacos-ci4s.argo.svc:8848 + server-addr: 172.20.32.181:31203 username: nacos password: h1n2x3j4y5@ retry: enabled: true + namespace: cc149503-5508-4da9-a735-9505e2a91363 config: + namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: nacos-ci4s.argo.svc:8848 + server-addr: 172.20.32.181:31203 # 配置文件格式 file-extension: yml # 共享配置 diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml index 4172ade8..e69f69d3 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml @@ -14,14 +14,16 @@ spring: nacos: discovery: # 服务注册地址 - server-addr: nacos-ci4s.argo.svc:8848 + namespace: cc149503-5508-4da9-a735-9505e2a91363 + server-addr: 172.20.32.181:31203 username: nacos password: h1n2x3j4y5@ config: + namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: nacos-ci4s.argo.svc:8848 + server-addr: 172.20.32.181:31203 # 配置文件格式 file-extension: yml # 共享配置 From bb48515532df143774c3eb9586c641a6fb7cd5da Mon Sep 17 00:00:00 2001 From: cp3hnu Date: Tue, 5 Nov 2024 15:25:36 +0800 Subject: [PATCH 2/3] feat: oauth2.0 --- react-ui/config/routes.ts | 10 + react-ui/src/app.tsx | 19 +- react-ui/src/components/IFramePage/index.tsx | 3 + .../RightContent/AvatarDropdown.tsx | 7 + react-ui/src/pages/Authorize/index.less | 0 react-ui/src/pages/Authorize/index.tsx | 50 ++++ react-ui/src/pages/GitLink/index.tsx | 7 + react-ui/src/pages/User/Login/index.tsx | 275 ++++++++++-------- react-ui/src/services/auth/index.js | 16 + react-ui/src/types.ts | 12 + react-ui/src/utils/sessionStorage.ts | 2 + react-ui/src/utils/ui.tsx | 13 +- 12 files changed, 277 insertions(+), 137 deletions(-) create mode 100644 react-ui/src/pages/Authorize/index.less create mode 100644 react-ui/src/pages/Authorize/index.tsx create mode 100644 react-ui/src/pages/GitLink/index.tsx create mode 100644 react-ui/src/services/auth/index.js diff --git a/react-ui/config/routes.ts b/react-ui/config/routes.ts index 66de518b..0639985b 100644 --- a/react-ui/config/routes.ts +++ b/react-ui/config/routes.ts @@ -27,6 +27,16 @@ export default [ }, ], }, + { + path: '/authorize', + layout: false, + component: './Authorize/index', + }, + { + path: '/gitlink', + layout: true, + component: './GitLink/index', + }, { path: '/user', layout: false, diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx index 4f911013..c018253e 100644 --- a/react-ui/src/app.tsx +++ b/react-ui/src/app.tsx @@ -7,7 +7,6 @@ import defaultSettings from '../config/defaultSettings'; import '../public/fonts/font.css'; import { getAccessToken } from './access'; import './dayjsConfig'; -import { PageEnum } from './enums/pagesEnums'; import './global.less'; import { removeAllPageCacheState } from './hooks/pageCacheState'; import { @@ -23,6 +22,7 @@ export { requestConfig as request } from './requestConfig'; import { type GlobalInitialState } from '@/types'; import { menuItemRender } from '@/utils/menuRender'; import ErrorBoundary from './components/ErrorBoundary'; +import { needAuth } from './utils'; import { gotoLoginPage } from './utils/ui'; /** @@ -40,14 +40,17 @@ export async function getInitialState(): Promise { roleNames: response.user.roles, } as API.CurrentUser; } catch (error) { - console.error(error); + console.error('1111', error); gotoLoginPage(); } return undefined; }; + // 如果不是登录页面,执行 const { location } = history; - if (location.pathname !== PageEnum.LOGIN) { + + console.log('getInitialState', needAuth(location.pathname)); + if (needAuth(location.pathname)) { const currentUser = await fetchUserInfo(); return { fetchUserInfo, @@ -94,7 +97,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { onPageChange: () => { const { location } = history; // 如果没有登录,重定向到 login - if (!initialState?.currentUser && location.pathname !== PageEnum.LOGIN) { + if (!initialState?.currentUser && needAuth(location.pathname)) { gotoLoginPage(); } }, @@ -159,8 +162,8 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => { export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { const { location } = e; const menus = getRemoteMenu(); - // console.log('onRouteChange', e); - if (menus === null && location.pathname !== PageEnum.LOGIN) { + console.log('onRouteChange', menus); + if (menus === null && needAuth(location.pathname)) { history.go(0); } }; @@ -170,12 +173,12 @@ export const patchRoutes: RuntimeConfig['patchRoutes'] = (e) => { }; export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => { - //console.log('patchClientRoutes', e); + console.log('patchClientRoutes', e); patchRouteWithRemoteMenus(e.routes); }; export function render(oldRender: () => void) { - // console.log('render'); + console.log('render'); const token = getAccessToken(); if (!token || token?.length === 0) { oldRender(); diff --git a/react-ui/src/components/IFramePage/index.tsx b/react-ui/src/components/IFramePage/index.tsx index c4ab9d3f..ce3e1c48 100644 --- a/react-ui/src/components/IFramePage/index.tsx +++ b/react-ui/src/components/IFramePage/index.tsx @@ -12,6 +12,7 @@ export enum IframePageType { DatasetAnnotation = 'DatasetAnnotation', // 数据标注 AppDevelopment = 'AppDevelopment', // 应用开发 DevEnv = 'DevEnv', // 开发环境 + GitLink = 'GitLink', } const getRequestAPI = (type: IframePageType): (() => Promise) => { @@ -26,6 +27,8 @@ const getRequestAPI = (type: IframePageType): (() => Promise) => { code: 200, data: SessionStorage.getItem(SessionStorage.editorUrlKey) || '', }); + case IframePageType.GitLink: + return () => Promise.resolve({ code: 200, data: 'http://172.20.32.201:4000' }); } }; diff --git a/react-ui/src/components/RightContent/AvatarDropdown.tsx b/react-ui/src/components/RightContent/AvatarDropdown.tsx index 5f8d639a..d93d4f74 100644 --- a/react-ui/src/components/RightContent/AvatarDropdown.tsx +++ b/react-ui/src/components/RightContent/AvatarDropdown.tsx @@ -1,6 +1,8 @@ import { clearSessionToken } from '@/access'; import { setRemoteMenu } from '@/services/session'; import { logout } from '@/services/system/auth'; +import { ClientInfo } from '@/types'; +import SessionStorage from '@/utils/sessionStorage'; import { gotoLoginPage } from '@/utils/ui'; import { LogoutOutlined, UserOutlined } from '@ant-design/icons'; import { setAlpha } from '@ant-design/pro-components'; @@ -64,6 +66,11 @@ const AvatarDropdown: React.FC = ({ menu }) => { clearSessionToken(); setRemoteMenu(null); gotoLoginPage(); + const clientInfo: ClientInfo = SessionStorage.getItem(SessionStorage.clientInfoKey, true); + if (clientInfo) { + const { logoutUri } = clientInfo; + location.replace(logoutUri); + } }; const actionClassName = useEmotionCss(({ token }) => { return { diff --git a/react-ui/src/pages/Authorize/index.less b/react-ui/src/pages/Authorize/index.less new file mode 100644 index 00000000..e69de29b diff --git a/react-ui/src/pages/Authorize/index.tsx b/react-ui/src/pages/Authorize/index.tsx new file mode 100644 index 00000000..f3624f32 --- /dev/null +++ b/react-ui/src/pages/Authorize/index.tsx @@ -0,0 +1,50 @@ +import { setSessionToken } from '@/access'; +import { loginByOauth2Req } from '@/services/auth'; +import { to } from '@/utils/promise'; +import { history, useModel, useSearchParams } from '@umijs/max'; +import { message } from 'antd'; +import { useEffect } from 'react'; +import { flushSync } from 'react-dom'; +import styles from './index.less'; + +function Authorize() { + const { initialState, setInitialState } = useModel('@@initialState'); + const [searchParams] = useSearchParams(); + const code = searchParams.get('code'); + const redirect = searchParams.get('redirect'); + useEffect(() => { + loginByOauth2(); + }, []); + + // 登录 + const loginByOauth2 = async () => { + const params = { + code, + }; + const [res] = await to(loginByOauth2Req(params)); + debugger; + if (res && res.data) { + const { access_token, expires_in } = res.data; + setSessionToken(access_token, access_token, expires_in); + message.success('登录成功!'); + await fetchUserInfo(); + history.push(redirect || '/'); + } + }; + + const fetchUserInfo = async () => { + const userInfo = await initialState?.fetchUserInfo?.(); + if (userInfo) { + flushSync(() => { + setInitialState((s) => ({ + ...s, + currentUser: userInfo, + })); + }); + } + }; + + return
; +} + +export default Authorize; diff --git a/react-ui/src/pages/GitLink/index.tsx b/react-ui/src/pages/GitLink/index.tsx new file mode 100644 index 00000000..8646926d --- /dev/null +++ b/react-ui/src/pages/GitLink/index.tsx @@ -0,0 +1,7 @@ +import IframePage, { IframePageType } from '@/components/IFramePage'; + +function GitLink() { + return ; +} + +export default GitLink; diff --git a/react-ui/src/pages/User/Login/index.tsx b/react-ui/src/pages/User/Login/index.tsx index 9ef18148..86d56ff6 100644 --- a/react-ui/src/pages/User/Login/index.tsx +++ b/react-ui/src/pages/User/Login/index.tsx @@ -1,11 +1,12 @@ import { clearSessionToken, setSessionToken } from '@/access'; +import { getClientInfoReq } from '@/services/auth'; import { getCaptchaImg, login } from '@/services/system/auth'; -import { parseJsonText } from '@/utils'; -import { safeInvoke } from '@/utils/functional'; import LocalStorage from '@/utils/localStorage'; import { to } from '@/utils/promise'; +import SessionStorage from '@/utils/sessionStorage'; +import { gotoOAuth2 } from '@/utils/ui'; import { history, useModel } from '@umijs/max'; -import { Button, Checkbox, Flex, Form, Image, Input, message, type InputRef } from 'antd'; +import { Form, message, type InputRef } from 'antd'; import CryptoJS from 'crypto-js'; import { useEffect, useRef, useState } from 'react'; import { flushSync } from 'react-dom'; @@ -31,25 +32,35 @@ const Login = () => { const captchaInputRef = useRef(null); useEffect(() => { - getCaptchaCode(); - const autoLogin = LocalStorage.getItem(LocalStorage.rememberPasswordKey) ?? 'false'; - if (autoLogin === 'true') { - const userStorage = LocalStorage.getItem(LocalStorage.loginUserKey); - const userJson = safeInvoke((text: string) => - CryptoJS.AES.decrypt(text, AESKEY).toString(CryptoJS.enc.Utf8), - )(userStorage); - const user = safeInvoke(parseJsonText)(userJson); - if (user && typeof user === 'object' && user.version === VERSION) { - const { username, password } = user; - form.setFieldsValue({ username: username, password: password, autoLogin: true }); - } else { - form.setFieldsValue({ username: '', password: '', autoLogin: true }); - LocalStorage.removeItem(LocalStorage.loginUserKey); - } - } else { - form.setFieldsValue({ username: '', password: '', autoLogin: false }); - } + // getCaptchaCode(); + // const autoLogin = LocalStorage.getItem(LocalStorage.rememberPasswordKey) ?? 'false'; + // if (autoLogin === 'true') { + // const userStorage = LocalStorage.getItem(LocalStorage.loginUserKey); + // const userJson = safeInvoke((text: string) => + // CryptoJS.AES.decrypt(text, AESKEY).toString(CryptoJS.enc.Utf8), + // )(userStorage); + // const user = safeInvoke(parseJsonText)(userJson); + // if (user && typeof user === 'object' && user.version === VERSION) { + // const { username, password } = user; + // form.setFieldsValue({ username: username, password: password, autoLogin: true }); + // } else { + // form.setFieldsValue({ username: '', password: '', autoLogin: true }); + // LocalStorage.removeItem(LocalStorage.loginUserKey); + // } + // } else { + // form.setFieldsValue({ username: '', password: '', autoLogin: false }); + // } + getClientInfo(); }, []); + const getClientInfo = async () => { + const [res] = await to(getClientInfoReq()); + if (res && res.data) { + const clientInfo = res.data; + SessionStorage.setItem(SessionStorage.clientInfoKey, clientInfo, true); + gotoOAuth2(); + } + }; + const getCaptchaCode = async () => { const [res] = await to(getCaptchaImg()); if (res) { @@ -71,6 +82,12 @@ const Login = () => { } }; + const handleSubmit2 = async (values: API.LoginParams) => { + const url = + 'http://172.20.32.106:8080/oauth/authorize?client_id=ci4s&response_type=code&grant_type=authorization_code'; + window.location.href = url; + }; + // 登录 const handleSubmit = async (values: API.LoginParams) => { const [res, error] = await to(login({ ...values, uuid })); @@ -109,113 +126,115 @@ const Login = () => { } }; - return ( -
-
-
- - 智能材料科研平台 -
-
- 智能材料科研平台 - -
-
- 大语言模型运维 统一管理平台 -
- -
-
-
-
- 欢迎登录 - 智能材料科研平台 -
-
-
账号登录
-
-
- - } - allowClear - /> - - - - } - allowClear - /> - - - -
- - - } - ref={captchaInputRef} - allowClear - /> - -
- 验证码 getCaptchaCode()} - /> -
- - - 记住密码 - - - - - -
-
-
-
-
-
- ); + return
; + + // return ( + //
+ //
+ //
+ // + // 智能材料科研平台 + //
+ //
+ // 智能材料科研平台 + // + //
+ //
+ // 大语言模型运维 统一管理平台 + //
+ // + //
+ //
+ //
+ //
+ // 欢迎登录 + // 智能材料科研平台 + //
+ //
+ //
账号登录
+ //
+ //
+ // + // } + // allowClear + // /> + // + + // + // } + // allowClear + // /> + // + + // + //
+ // + // + // } + // ref={captchaInputRef} + // allowClear + // /> + // + //
+ // 验证码 getCaptchaCode()} + // /> + //
+ + // + // 记住密码 + // + + // + // + // + //
+ //
+ //
+ //
+ //
+ //
+ // ); }; export default Login; diff --git a/react-ui/src/services/auth/index.js b/react-ui/src/services/auth/index.js new file mode 100644 index 00000000..853ad6ab --- /dev/null +++ b/react-ui/src/services/auth/index.js @@ -0,0 +1,16 @@ +import { request } from '@umijs/max'; + +// 单点登录 +export function loginByOauth2Req(data) { + return request(`/api/auth/loginByOauth2`, { + method: 'POST', + data, + }); +} + +// 登录获取客户端信息 +export function getClientInfoReq() { + return request(`/api/auth/oauth2ClientInfo`, { + method: 'GET', + }); +} \ No newline at end of file diff --git a/react-ui/src/types.ts b/react-ui/src/types.ts index 40348fab..a7df0561 100644 --- a/react-ui/src/types.ts +++ b/react-ui/src/types.ts @@ -7,6 +7,17 @@ import { ExperimentStatus, TensorBoardStatus } from '@/enums'; import type { Settings as LayoutSettings } from '@ant-design/pro-components'; +export type ClientInfo = { + accessTokenUri: string; + checkTokenUri: string; + clientId: string; + clientSecret: string; + loginPage: string; + logoutUri: string; + redirectUri: string; + userAuthorizationUri: string; +}; + // 全局初始状态类型 export type GlobalInitialState = { settings?: Partial; @@ -14,6 +25,7 @@ export type GlobalInitialState = { fetchUserInfo?: () => Promise; loading?: boolean; collapsed?: boolean; + clientInfo?: ClientInfo; }; // 流水线全局参数 diff --git a/react-ui/src/utils/sessionStorage.ts b/react-ui/src/utils/sessionStorage.ts index 41117e1e..8ffa8836 100644 --- a/react-ui/src/utils/sessionStorage.ts +++ b/react-ui/src/utils/sessionStorage.ts @@ -9,6 +9,8 @@ export default class SessionStorage { static readonly serviceVersionInfoKey = 'service-version-info'; // 编辑器 url static readonly editorUrlKey = 'editor-url'; + // 客户端信息 + static readonly clientInfoKey = 'client-info'; static getItem(key: string, isObject: boolean = false) { const jsonStr = sessionStorage.getItem(key); diff --git a/react-ui/src/utils/ui.tsx b/react-ui/src/utils/ui.tsx index 9034a67b..12f013f2 100644 --- a/react-ui/src/utils/ui.tsx +++ b/react-ui/src/utils/ui.tsx @@ -6,9 +6,11 @@ import { PageEnum } from '@/enums/pagesEnums'; import { removeAllPageCacheState } from '@/hooks/pageCacheState'; import themes from '@/styles/theme.less'; +import { type ClientInfo } from '@/types'; import { history } from '@umijs/max'; import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; import { closeAllModals } from './modal'; +import SessionStorage from './sessionStorage'; type ModalConfirmProps = ModalFuncProps & { isDelete?: boolean; @@ -75,7 +77,7 @@ export const gotoLoginPage = (toHome: boolean = true) => { const { pathname, search } = location; const urlParams = new URLSearchParams(); urlParams.append('redirect', pathname + search); - const newSearch = toHome && pathname !== '/' ? '' : urlParams.toString(); + const newSearch = toHome || pathname === '/' ? '' : urlParams.toString(); // console.log('pathname', pathname); // console.log('search', search); if (pathname !== PageEnum.LOGIN) { @@ -88,6 +90,15 @@ export const gotoLoginPage = (toHome: boolean = true) => { } }; +export const gotoOAuth2 = () => { + const clientInfo = SessionStorage.getItem(SessionStorage.clientInfoKey, true) as ClientInfo; + if (clientInfo) { + const { clientId, userAuthorizationUri } = clientInfo; + const url = `${userAuthorizationUri}?client_id=${clientId}&response_type=code&grant_type=authorization_code`; + location.replace(url); + } +}; + /** * 验证文件上传 * From e571fe6f423fa15736fb5360ffcffc4829acb30f Mon Sep 17 00:00:00 2001 From: liuhuazhong Date: Wed, 6 Nov 2024 14:42:45 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-auth/src/main/resources/bootstrap.yml | 22 ++++--------------- .../src/main/resources/bootstrap.yml | 10 ++------- .../src/main/resources/bootstrap.yml | 6 ++--- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ruoyi-auth/src/main/resources/bootstrap.yml b/ruoyi-auth/src/main/resources/bootstrap.yml index 625b9b8f..95a9dfdd 100644 --- a/ruoyi-auth/src/main/resources/bootstrap.yml +++ b/ruoyi-auth/src/main/resources/bootstrap.yml @@ -13,19 +13,17 @@ spring: cloud: nacos: discovery: - namespace: cc149503-5508-4da9-a735-9505e2a91363 # 服务注册地址 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 username: nacos password: h1n2x3j4y5@ retry: enabled: true config: - namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 # 配置文件格式 file-extension: yml # 共享配置 @@ -38,18 +36,6 @@ spring: refresh: true redis: - host: 172.20.32.88 + host: 172.20.32.150 port: 6379 - password: - -oauth2: - url: http://172.20.32.106:8080 -# url: http://110.41.62.43:8088 - client-id: ci4s - client-secret: 123456 - user-authorization-uri: ${oauth2.url}/oauth/authorize - access-token-uri: ${oauth2.url}/oauth/token - logout-uri: ${oauth2.url}/logout - redirect-uri: http://172.20.32.83:8002/authorize - check-token-uri: ${oauth2.url}/oauth/check_token - login-page: ${oauth2.url}/login.html + password: \ No newline at end of file diff --git a/ruoyi-gateway/src/main/resources/bootstrap.yml b/ruoyi-gateway/src/main/resources/bootstrap.yml index 7f060cdc..bcfd24d8 100644 --- a/ruoyi-gateway/src/main/resources/bootstrap.yml +++ b/ruoyi-gateway/src/main/resources/bootstrap.yml @@ -4,10 +4,6 @@ server: # Spring spring: - redis: - host: 172.20.32.88 - port: 6379 - password: application: # 应用名称 name: ruoyi-gateway @@ -18,18 +14,16 @@ spring: nacos: discovery: # 服务注册地址 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 username: nacos password: h1n2x3j4y5@ retry: enabled: true - namespace: cc149503-5508-4da9-a735-9505e2a91363 config: - namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 # 配置文件格式 file-extension: yml # 共享配置 diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml index e69f69d3..4172ade8 100644 --- a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml +++ b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml @@ -14,16 +14,14 @@ spring: nacos: discovery: # 服务注册地址 - namespace: cc149503-5508-4da9-a735-9505e2a91363 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 username: nacos password: h1n2x3j4y5@ config: - namespace: cc149503-5508-4da9-a735-9505e2a91363 username: nacos password: h1n2x3j4y5@ # 配置中心地址 - server-addr: 172.20.32.181:31203 + server-addr: nacos-ci4s.argo.svc:8848 # 配置文件格式 file-extension: yml # 共享配置