package com.ruoyi.web.controller.sso; 
 | 
  
 | 
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 com.smartor.domain.SSOUserInfo; 
 | 
import lombok.extern.slf4j.Slf4j; 
 | 
import org.springframework.beans.factory.annotation.Autowired; 
 | 
import org.springframework.beans.factory.annotation.Value; 
 | 
import org.springframework.http.*; 
 | 
import org.springframework.http.client.SimpleClientHttpRequestFactory; 
 | 
import org.springframework.util.LinkedMultiValueMap; 
 | 
import org.springframework.util.MultiValueMap; 
 | 
import org.springframework.web.bind.annotation.GetMapping; 
 | 
import org.springframework.web.bind.annotation.RequestMapping; 
 | 
import org.springframework.web.bind.annotation.RequestParam; 
 | 
import org.springframework.web.bind.annotation.RestController; 
 | 
import org.springframework.web.client.RestTemplate; 
 | 
  
 | 
import javax.servlet.http.HttpServletRequest; 
 | 
import javax.servlet.http.HttpServletResponse; 
 | 
import java.io.IOException; 
 | 
import java.net.URLEncoder; 
 | 
import java.util.HashMap; 
 | 
import java.util.Map; 
 | 
  
 | 
/** 
 | 
 * SSO单点登录控制器 
 | 
 */ 
 | 
@RestController 
 | 
@RequestMapping("/sso") 
 | 
@Slf4j 
 | 
public class SSOController { 
 | 
  
 | 
    @Value("${sso.client_id}") 
 | 
    private String clientId; 
 | 
  
 | 
    @Value("${sso.client_secret}") 
 | 
    private String clientSecret; 
 | 
  
 | 
    @Value("${sso.internal.authorize_url}") 
 | 
    private String internalAuthorizeUrl; 
 | 
  
 | 
    @Value("${sso.internal.token_url}") 
 | 
    private String internalTokenUrl; 
 | 
  
 | 
    @Value("${sso.internal.userinfo_url}") 
 | 
    private String internalUserinfoUrl; 
 | 
  
 | 
    @Value("${sso.internal.redirect_uri}") 
 | 
    private String internalRedirectUri; 
 | 
  
 | 
    @Value("${sso.external.authorize_url}") 
 | 
    private String externalAuthorizeUrl; 
 | 
  
 | 
    @Value("${sso.external.token_url}") 
 | 
    private String externalTokenUrl; 
 | 
  
 | 
    @Value("${sso.external.userinfo_url}") 
 | 
    private String externalUserinfoUrl; 
 | 
  
 | 
    @Value("${sso.external.redirect_uri}") 
 | 
    private String externalRedirectUri; 
 | 
  
 | 
    @Value("${sso.state}") 
 | 
    private String state; 
 | 
  
 | 
    @Value("${sso.scope}") 
 | 
    private String scope; 
 | 
  
 | 
    @Autowired 
 | 
    private ISysUserService userService; 
 | 
  
 | 
    @Autowired 
 | 
    private TokenService tokenService; 
 | 
  
 | 
    private final RestTemplate restTemplate; 
 | 
  
 | 
    public SSOController() { 
 | 
        // 配置RestTemplate超时 
 | 
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 
 | 
        factory.setConnectTimeout(10000); // 连接超时10秒 
 | 
        factory.setReadTimeout(30000);    // 读取超时30秒 
 | 
        this.restTemplate = new RestTemplate(factory); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * SSO登录入口 - 信通院会调用这个地址 
 | 
     * 访问路径:http://域名:8095/sso/login 
 | 
     */ 
 | 
    @GetMapping("") 
 | 
    public void ssoLogin() { 
 | 
        log.info("收到SSO登录请求,开始重定向到授权服务器"); 
 | 
  
 | 
        // 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 { 
 | 
            SSOTokenResponse accessToken = getAccessToken(code, true); 
 | 
            SSOUserInfo userInfo = getUserInfo(accessToken.getAccess_token(), true); 
 | 
  
 | 
            createLocalSession(userInfo); 
 | 
  
 | 
        } catch (Exception e) { 
 | 
            e.printStackTrace(); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    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; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取访问令牌 
 | 
     */ 
 | 
    private SSOTokenResponse getAccessToken(String code, boolean isInternal) throws Exception { 
 | 
        HttpHeaders headers = new HttpHeaders(); 
 | 
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 
 | 
  
 | 
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); 
 | 
        params.add("client_id", clientId); 
 | 
        params.add("client_secret", clientSecret); 
 | 
        params.add("code", code); 
 | 
        params.add("grant_type", "authorization_code"); 
 | 
        params.add("redirect_uri", getRedirectUri(isInternal)); 
 | 
  
 | 
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers); 
 | 
  
 | 
        ResponseEntity<String> response = restTemplate.exchange(getTokenUrl(isInternal), HttpMethod.POST, request, String.class); 
 | 
  
 | 
        log.info("Token响应: {}", response.getBody()); 
 | 
  
 | 
        if (response.getBody() == null || response.getBody().trim().isEmpty()) { 
 | 
            throw new RuntimeException("Token响应为空"); 
 | 
        } 
 | 
  
 | 
        SSOTokenResponse tokenResponse = JSON.parseObject(response.getBody(), SSOTokenResponse.class); 
 | 
  
 | 
        if (tokenResponse == null || StringUtils.isEmpty(tokenResponse.getAccess_token())) { 
 | 
            throw new RuntimeException("获取access_token失败"); 
 | 
        } 
 | 
  
 | 
        return tokenResponse; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取用户信息 
 | 
     */ 
 | 
    private SSOUserInfo getUserInfo(String accessToken, boolean isInternal) throws Exception { 
 | 
        HttpHeaders headers = new HttpHeaders(); 
 | 
        headers.set("Authorization", "Bearer " + accessToken); 
 | 
  
 | 
        HttpEntity<String> entity = new HttpEntity<>(headers); 
 | 
  
 | 
        ResponseEntity<String> response = restTemplate.exchange(getUserinfoUrl(isInternal), HttpMethod.GET, entity, String.class); 
 | 
  
 | 
        log.info("用户信息响应: {}", response.getBody()); 
 | 
  
 | 
        if (response.getBody() == null || response.getBody().trim().isEmpty()) { 
 | 
            throw new RuntimeException("用户信息响应为空"); 
 | 
        } 
 | 
  
 | 
        SSOUserInfo userInfo = JSON.parseObject(response.getBody(), SSOUserInfo.class); 
 | 
  
 | 
        if (userInfo == null || StringUtils.isEmpty(userInfo.getName())) { 
 | 
            throw new RuntimeException("获取用户信息失败或用户名为空"); 
 | 
        } 
 | 
  
 | 
        return userInfo; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 创建本地会话 
 | 
     */ 
 | 
    private String createLocalSession(SSOUserInfo userInfo) { 
 | 
        // 根据SSO用户信息查找本地用户(根据工号匹配) 
 | 
        SysUser localUser = findLocalUserByName(userInfo.getName()); 
 | 
  
 | 
        if (localUser == null) { 
 | 
            throw new RuntimeException("用户不存在或未开通系统权限:" + userInfo.getName()); 
 | 
        } 
 | 
  
 | 
        // 创建登录用户对象 
 | 
        LoginUser loginUser = new LoginUser(localUser.getUserId(), localUser.getDeptId(), localUser, null); 
 | 
  
 | 
        // 生成token 
 | 
        String token = tokenService.createToken(loginUser); 
 | 
        log.info("生成的token为:{}", token); 
 | 
        return token; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据工号查找本地用户 
 | 
     */ 
 | 
    private SysUser findLocalUserByName(String workNumber) { 
 | 
        if (StringUtils.isEmpty(workNumber)) { 
 | 
            log.error("工号为空,无法查找用户"); 
 | 
            return null; 
 | 
        } 
 | 
  
 | 
        try { 
 | 
            SysUser user = userService.selectUserByUserName(workNumber); 
 | 
            if (user != null) { 
 | 
                log.info("找到用户: {} - {}", workNumber, user.getNickName()); 
 | 
            } else { 
 | 
                log.warn("未找到用户: {}", workNumber); 
 | 
            } 
 | 
            return user; 
 | 
        } catch (Exception e) { 
 | 
            log.error("查询用户失败: {}", workNumber, e); 
 | 
            return null; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据客户端IP判断是否为内网 
 | 
     */ 
 | 
    private boolean isInternalNetwork(String clientIp) { 
 | 
        if (clientIp == null || clientIp.isEmpty()) { 
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        // 判断是否为内网网段 10.10.13.* 
 | 
        return clientIp.startsWith("10.10.13."); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 获取客户端真实IP 
 | 
     */ 
 | 
    private String getClientIp(HttpServletRequest request) { 
 | 
        String ip = request.getHeader("X-Forwarded-For"); 
 | 
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { 
 | 
            ip = request.getHeader("Proxy-Client-IP"); 
 | 
        } 
 | 
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { 
 | 
            ip = request.getHeader("WL-Proxy-Client-IP"); 
 | 
        } 
 | 
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { 
 | 
            ip = request.getHeader("HTTP_CLIENT_IP"); 
 | 
        } 
 | 
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { 
 | 
            ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 
 | 
        } 
 | 
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { 
 | 
            ip = request.getRemoteAddr(); 
 | 
        } 
 | 
  
 | 
        // 如果有多个IP,取第一个 
 | 
        if (ip != null && ip.contains(",")) { 
 | 
            ip = ip.split(",")[0].trim(); 
 | 
        } 
 | 
  
 | 
        log.info("客户端IP: {}", ip); 
 | 
        return ip; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据网络环境获取授权URL 
 | 
     */ 
 | 
    private String getAuthorizeUrl(boolean isInternal) { 
 | 
        return isInternal ? internalAuthorizeUrl : externalAuthorizeUrl; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据网络环境获取Token URL 
 | 
     */ 
 | 
    private String getTokenUrl(boolean isInternal) { 
 | 
        return isInternal ? internalTokenUrl : externalTokenUrl; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据网络环境获取用户信息URL 
 | 
     */ 
 | 
    private String getUserinfoUrl(boolean isInternal) { 
 | 
        return isInternal ? internalUserinfoUrl : externalUserinfoUrl; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 根据网络环境获取回调URI 
 | 
     */ 
 | 
    private String getRedirectUri(boolean isInternal) { 
 | 
        return isInternal ? internalRedirectUri : externalRedirectUri; 
 | 
    } 
 | 
  
 | 
  
 | 
} 
 |