| | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.ruoyi.common.core.domain.entity.SysUser; |
| | | import com.ruoyi.common.core.domain.model.LoginUser; |
| | | import com.ruoyi.common.utils.HttpUtil; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.framework.web.service.TokenService; |
| | | import com.ruoyi.system.service.ISysUserService; |
| | | import com.smartor.domain.SSOTokenResponse; |
| | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.net.URLEncoder; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * SSO单点登录控制器 |
| | |
| | | * 访问路径:http://域名:8095/sso/login |
| | | */ |
| | | @GetMapping("") |
| | | public void ssoLogin(HttpServletResponse response, HttpServletRequest request) throws IOException { |
| | | public void ssoLogin() { |
| | | log.info("收到SSO登录请求,开始重定向到授权服务器"); |
| | | |
| | | // 获取客户端IP |
| | | String clientIp = getClientIp(request); |
| | | boolean isInternal = isInternalNetwork(clientIp); |
| | | |
| | | // 构建授权URL |
| | | String authUrl = buildAuthorizationUrl(isInternal); |
| | | log.info("重定向到授权URL: {}", authUrl); |
| | | |
| | | response.sendRedirect(authUrl); |
| | | } |
| | | |
| | | /** |
| | | * SSO回调处理 |
| | | */ |
| | | @GetMapping("/callback") |
| | | public void ssoCallback(@RequestParam(required = false) String code, |
| | | @RequestParam(required = false) String state, |
| | | @RequestParam(required = false) String error, |
| | | HttpServletResponse response, |
| | | HttpServletRequest request) throws IOException { |
| | | |
| | | log.info("收到SSO回调,code: {}, state: {}, error: {}", code, state, error); |
| | | |
| | | if (error != null) { |
| | | log.error("SSO授权失败: {}", error); |
| | | try { |
| | | response.sendRedirect("/login?sso_error=" + URLEncoder.encode(error, "UTF-8")); |
| | | } catch (Exception e) { |
| | | log.error("重定向失败", e); |
| | | response.sendRedirect("/login?sso_error=unknown_error"); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (code == null || !this.state.equals(state)) { |
| | | log.error("SSO回调参数错误,code: {}, state: {}", code, state); |
| | | response.sendRedirect("/login?sso_error=invalid_callback"); |
| | | return; |
| | | } |
| | | |
| | | // Authorize鉴权接口 |
| | | String param = "client_id=" + clientId + "&redirect_uri=" + internalRedirectUri + "&response_type=code" + "&state=" + state + "&scope=" + scope; |
| | | log.info("【Authorize鉴权接口】入参为:{}", param); |
| | | String s = HttpUtils.sendGet(internalAuthorizeUrl, param); |
| | | Map<String, String> result = getResult(s); |
| | | String code = result.get("code"); |
| | | try { |
| | | // 获取客户端IP |
| | | String clientIp = getClientIp(request); |
| | | boolean isInternal = isInternalNetwork(clientIp); |
| | | SSOTokenResponse accessToken = getAccessToken(code, true); |
| | | SSOUserInfo userInfo = getUserInfo(accessToken.getAccess_token(), true); |
| | | |
| | | // 1. 用code换取access_token |
| | | SSOTokenResponse tokenResponse = getAccessToken(code, isInternal); |
| | | log.info("获取到access_token: {}", tokenResponse.getAccess_token()); |
| | | createLocalSession(userInfo); |
| | | |
| | | // 2. 用access_token获取用户信息 |
| | | SSOUserInfo userInfo = getUserInfo(tokenResponse.getAccess_token(), isInternal); |
| | | log.info("获取到用户信息: {}", userInfo); |
| | | |
| | | // 3. 根据用户信息创建本地会话 |
| | | String token = createLocalSession(userInfo); |
| | | |
| | | // 4. 重定向到前端首页,携带token |
| | | String frontendUrl = "/#/index?token=" + token; |
| | | response.sendRedirect(frontendUrl); |
| | | |
| | | } catch (RuntimeException e) { |
| | | log.error("SSO业务处理失败: {}", e.getMessage(), e); |
| | | try { |
| | | response.sendRedirect("/login?sso_error=" + URLEncoder.encode(e.getMessage(), "UTF-8")); |
| | | } catch (Exception ex) { |
| | | log.error("重定向失败", ex); |
| | | response.sendRedirect("/login?sso_error=system_error"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("SSO登录处理失败", e); |
| | | response.sendRedirect("/login?sso_error=login_failed"); |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 构建授权URL |
| | | */ |
| | | private String buildAuthorizationUrl(boolean isInternal) { |
| | | try { |
| | | String redirectUri = getRedirectUri(isInternal); |
| | | return getAuthorizeUrl(isInternal) + "?" + |
| | | "client_id=" + clientId + |
| | | "&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8") + |
| | | "&response_type=code" + |
| | | "&state=" + state + |
| | | "&scope=" + URLEncoder.encode(scope, "UTF-8"); |
| | | } catch (Exception e) { |
| | | log.error("构建授权URL失败", e); |
| | | throw new RuntimeException("构建授权URL失败", e); |
| | | private Map<String, String> getResult(String param) { |
| | | Map<String, String> paramMap = new HashMap<>(); |
| | | |
| | | if (param == null || !param.contains("?")) { |
| | | return paramMap; |
| | | } |
| | | |
| | | String query = param.substring(param.indexOf('?') + 1); |
| | | |
| | | String[] pairs = query.split("&"); |
| | | |
| | | for (String pair : pairs) { |
| | | String[] kv = pair.split("=", 2); |
| | | String key = kv[0]; |
| | | String value = kv.length > 1 ? kv[1] : ""; |
| | | paramMap.put(key, value); |
| | | } |
| | | return paramMap; |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); |
| | | |
| | | ResponseEntity<String> response = restTemplate.exchange( |
| | | getTokenUrl(isInternal), HttpMethod.POST, request, String.class); |
| | | ResponseEntity<String> response = restTemplate.exchange(getTokenUrl(isInternal), HttpMethod.POST, request, String.class); |
| | | |
| | | log.info("Token响应: {}", response.getBody()); |
| | | |
| | |
| | | |
| | | HttpEntity<String> entity = new HttpEntity<>(headers); |
| | | |
| | | ResponseEntity<String> response = restTemplate.exchange( |
| | | getUserinfoUrl(isInternal), HttpMethod.GET, entity, String.class); |
| | | ResponseEntity<String> response = restTemplate.exchange(getUserinfoUrl(isInternal), HttpMethod.GET, entity, String.class); |
| | | |
| | | log.info("用户信息响应: {}", response.getBody()); |
| | | |
| | |
| | | LoginUser loginUser = new LoginUser(localUser.getUserId(), localUser.getDeptId(), localUser, null); |
| | | |
| | | // 生成token |
| | | return tokenService.createToken(loginUser); |
| | | String token = tokenService.createToken(loginUser); |
| | | log.info("生成的token为:{}", token); |
| | | return token; |
| | | } |
| | | |
| | | /** |