Browse Source

集成单点登录

bihuisong 1 year ago
parent
commit
e17129e44a
20 changed files with 1248 additions and 282 deletions
  1. 16 19
      songhua-admin/src/main/java/com/songhua/web/controller/system/SysLoginController.java
  2. 1 1
      songhua-admin/src/main/resources/application.yml
  3. 11 4
      songhua-common/src/main/java/com/songhua/common/core/domain/model/LoginUser.java
  4. 1 1
      songhua-framework/src/main/java/com/songhua/framework/config/SecurityConfig.java
  5. 128 0
      songhua-framework/src/main/java/com/songhua/framework/sso/SsoLoginController.java
  6. 302 0
      songhua-framework/src/main/java/com/songhua/framework/sso/SsoRequestUtil.java
  7. 131 0
      songhua-framework/src/main/java/com/songhua/framework/sso/domain/SysLoginUser.java
  8. 231 0
      songhua-framework/src/main/java/com/songhua/framework/sso/utli/AjaxJson.java
  9. 63 0
      songhua-framework/src/main/java/com/songhua/framework/sso/utli/JsonUtil.java
  10. 65 0
      songhua-framework/src/main/java/com/songhua/framework/sso/utli/MyHttpSessionHolder.java
  11. 59 57
      songhua-framework/src/main/java/com/songhua/framework/web/service/SysLoginService.java
  12. 30 50
      songhua-framework/src/main/java/com/songhua/framework/web/service/TokenService.java
  13. 82 135
      songhua-system/src/main/java/com/songhua/system/service/impl/SysUserServiceImpl.java
  14. 1 1
      songhua-ui/package.json
  15. 39 0
      songhua-ui/src/api/sso/method-util.js
  16. 10 2
      songhua-ui/src/layout/components/Navbar.vue
  17. 13 12
      songhua-ui/src/permission.js
  18. 6 0
      songhua-ui/src/router/index.js
  19. 6 0
      songhua-ui/src/utils/auth.js
  20. 53 0
      songhua-ui/src/views/sso/sso-login.vue

+ 16 - 19
songhua-admin/src/main/java/com/songhua/web/controller/system/SysLoginController.java

@@ -1,12 +1,5 @@
 package com.songhua.web.controller.system;
 
-import java.util.List;
-import java.util.Set;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
 import com.songhua.common.constant.Constants;
 import com.songhua.common.core.domain.AjaxResult;
 import com.songhua.common.core.domain.entity.SysMenu;
@@ -16,15 +9,22 @@ import com.songhua.common.utils.SecurityUtils;
 import com.songhua.framework.web.service.SysLoginService;
 import com.songhua.framework.web.service.SysPermissionService;
 import com.songhua.system.service.ISysMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Set;
 
 /**
  * 登录验证
- * 
+ *
  * @author ruoyi
  */
 @RestController
-public class SysLoginController
-{
+public class SysLoginController {
     @Autowired
     private SysLoginService loginService;
 
@@ -36,13 +36,12 @@ public class SysLoginController
 
     /**
      * 登录方法
-     * 
+     *
      * @param loginBody 登录信息
      * @return 结果
      */
     @PostMapping("/login")
-    public AjaxResult login(@RequestBody LoginBody loginBody)
-    {
+    public AjaxResult login(@RequestBody LoginBody loginBody) {
         AjaxResult ajax = AjaxResult.success();
         // 生成令牌
         String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
@@ -53,12 +52,11 @@ public class SysLoginController
 
     /**
      * 获取用户信息
-     * 
+     *
      * @return 用户信息
      */
     @GetMapping("getInfo")
-    public AjaxResult getInfo()
-    {
+    public AjaxResult getInfo() {
         SysUser user = SecurityUtils.getLoginUser().getUser();
         // 角色集合
         Set<String> roles = permissionService.getRolePermission(user);
@@ -73,12 +71,11 @@ public class SysLoginController
 
     /**
      * 获取路由信息
-     * 
+     *
      * @return 路由信息
      */
     @GetMapping("getRouters")
-    public AjaxResult getRouters()
-    {
+    public AjaxResult getRouters() {
         Long userId = SecurityUtils.getUserId();
         List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
         return AjaxResult.success(menuService.buildMenus(menus));

+ 1 - 1
songhua-admin/src/main/resources/application.yml

@@ -72,7 +72,7 @@ spring:
     # 端口,默认为6379
     port: 16379
     # 数据库索引
-    database: 0
+    database: 1
     # 密码
     password: sooka123456
     # 连接超时时间

+ 11 - 4
songhua-common/src/main/java/com/songhua/common/core/domain/model/LoginUser.java

@@ -2,6 +2,7 @@ package com.songhua.common.core.domain.model;
 
 import com.alibaba.fastjson2.annotation.JSONField;
 import com.songhua.common.core.domain.entity.SysUser;
+import lombok.Data;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
 import java.util.Collection;
@@ -9,9 +10,10 @@ import java.util.Set;
 
 /**
  * 登录用户身份权限
- * 
+ *
  * @author ruoyi
  */
+@Data
 public class LoginUser implements UserDetails
 {
     private static final long serialVersionUID = 1L;
@@ -22,6 +24,11 @@ public class LoginUser implements UserDetails
     private Long userId;
 
     /**
+     * 用户名
+     */
+    private String username;
+
+    /**
      * 部门ID
      */
     private Long deptId;
@@ -144,7 +151,7 @@ public class LoginUser implements UserDetails
 
     /**
      * 指定用户是否解锁,锁定的用户无法进行身份验证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)
@@ -156,7 +163,7 @@ public class LoginUser implements UserDetails
 
     /**
      * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)
@@ -168,7 +175,7 @@ public class LoginUser implements UserDetails
 
     /**
      * 是否可用 ,禁用的用户不能身份验证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)

+ 1 - 1
songhua-framework/src/main/java/com/songhua/framework/config/SecurityConfig.java

@@ -111,7 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 // 过滤请求
                 .authorizeRequests()
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                .antMatchers("/login", "/register", "/captchaImage").permitAll()
+                .antMatchers("/login", "/register", "/captchaImage","/sso/doLoginByTicket","/sso/logout").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()

+ 128 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/SsoLoginController.java

@@ -0,0 +1,128 @@
+package com.songhua.framework.sso;
+
+
+import com.songhua.common.core.domain.AjaxResult;
+import com.songhua.common.core.domain.entity.SysUser;
+import com.songhua.common.core.domain.model.LoginUser;
+import com.songhua.common.utils.uuid.IdUtils;
+import com.songhua.framework.sso.utli.AjaxJson;
+import com.songhua.framework.sso.utli.MyHttpSessionHolder;
+import com.songhua.framework.web.service.SysLoginService;
+import com.songhua.framework.web.service.TokenService;
+import com.songhua.system.service.impl.SysUserServiceImpl;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+public class SsoLoginController {
+
+    @Resource
+    private SysUserServiceImpl sysUserServiceImpl;
+
+    @Resource
+    private SysLoginService sysLoginService;
+
+    @Resource
+    private TokenService tokenService;
+
+    @Resource
+    private SsoRequestUtil ssoRequestUtil;
+
+
+    // 返回SSO认证中心登录地址 (前后台分离环境下专用)
+    @RequestMapping("/sso/getSsoAuthUrl")
+    public AjaxJson getSsoAuthUrl(String clientLoginUrl) {
+        String serverAuthUrl = ssoRequestUtil.buildServerAuthUrl(clientLoginUrl);
+        return AjaxJson.getSuccessData(serverAuthUrl);
+    }
+
+    // 根据ticket进行登录(前后台分离环境下专用)
+    @RequestMapping("/sso/doLoginByTicket")
+    public AjaxResult doLoginByTicket(String ticket, HttpServletRequest request) {
+        Map map = new HashMap();
+        HttpSession session = MyHttpSessionHolder.getSession(request);
+        Object loginId = ssoRequestUtil.checkTicket(ticket, request, "/sso/doLoginByTicket");
+        if(loginId != null) {
+            session.setAttribute("userId", loginId);
+            session.setAttribute("user", ssoRequestUtil.getUserInfo(loginId));
+            /**此处处理一体化用户登录业务**/
+            SysUser user = sysUserServiceImpl.selectUserById(Long.parseLong(loginId.toString()));//此处根据sso端用户唯一标识来换取客户端用户与唯一标识,不局限于用户ID,只要能和sso端的loginId对应即可
+            if(ObjectUtils.isNotEmpty(user)){
+                String token = sysLoginService.ssoLogin(user.getUserName());
+                map.put("access_token", token);
+                map.put("expires_in", 30);
+                map.put("userinfo",user);
+                map.put("session_id", session.getId());
+            }
+            return AjaxResult.success(map);
+        }
+        return AjaxResult.error("无效ticket:" + ticket);
+    }
+
+    // getInfo接口 (只有登录后才可以调用此接口)
+    @RequestMapping("/getCurrInfo")
+    public Object getCurrInfo(HttpServletRequest request) {
+        // 如果没有登录,就返回特定信息
+        HttpSession session = MyHttpSessionHolder.getSession(request);
+        if(session.getAttribute("userId") == null) {
+            return AjaxJson.getNotLogin();
+        }
+        // 从Session中获取user对象
+        SysUser user = (SysUser) session.getAttribute("user");
+        return AjaxJson.getSuccessData(user);
+    }
+
+
+    // SSO-Client端:单点注销地址
+    @RequestMapping("/sso/logout")
+    public Object ssoLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+        // 如果未登录,则无需注销
+        HttpSession session = MyHttpSessionHolder.getSession(request);
+        if(session.getAttribute("userId") == null) {
+            return AjaxResult.success(ssoRequestUtil.getLogoutRedirectUrl());
+        }
+
+        // 调用 sso-server 认证中心单点注销API
+        Object loginId = session.getAttribute("userId");  // 账号id
+        String timestamp = String.valueOf(System.currentTimeMillis());	// 时间戳
+        String nonce = ssoRequestUtil.getRandomString(20);		// 随机字符串
+        String sign = ssoRequestUtil.getSign(loginId, timestamp, nonce, ssoRequestUtil.secretkey);	// 参数签名
+
+        String url = ssoRequestUtil.getSloUrl() +
+                "?loginId=" + loginId +
+                "&timestamp=" + timestamp +
+                "&nonce=" + nonce +
+                "&sign=" + sign;
+        AjaxJson result = ssoRequestUtil.request(url);
+
+        // 校验响应状态码,200 代表成功
+        if(result.getCode() == 200) {
+
+            // 极端场景下,sso-server 中心的单点注销可能并不会通知到此 client 端,所以这里需要再补一刀
+            session.removeAttribute("userId");
+            // 如果指定了 back 地址,则重定向,否则返回 JSON 信息
+            return AjaxResult.success("注销成功",ssoRequestUtil.getLogoutRedirectUrl());
+//            if(SsoRequestUtil.isNotEmpty(back)) {
+//                response.sendRedirect(back);
+//                return null;
+//            } else {
+//                return AjaxJson.getSuccess();
+//            }
+
+        } else {
+            // 将 sso-server 回应的消息作为异常抛出
+            throw new RuntimeException(result.getMsg());
+        }
+    }
+}

+ 302 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/SsoRequestUtil.java

@@ -0,0 +1,302 @@
+package com.songhua.framework.sso;
+
+import com.dtflys.forest.Forest;
+import com.songhua.framework.sso.domain.SysLoginUser;
+import com.songhua.framework.sso.utli.AjaxJson;
+import com.songhua.framework.sso.utli.JsonUtil;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.MessageDigest;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * 封装一些 sso 共用方法 
+ * 
+ * @author kong
+ * @since: 2022-4-30
+ */
+@Service
+public class SsoRequestUtil {
+
+
+    public String host = "http://127.0.0.1:17005";
+
+	/**
+	 * SSO-Server端 统一认证地址 
+	 */
+	public String authUrl;
+
+	public String getAuthUrl() {
+		return authUrl = host + "/sso/auth";
+	}
+	/**
+	 * 使用 Http 请求校验ticket 
+	 */
+//	public static boolean isHttp = true;
+	
+	/**
+	 * SSO-Server端 ticket校验地址 
+	 */
+	public String checkTicketUrl;
+
+	public String getCheckTicketUrl() {
+		return checkTicketUrl = host + "/sso/checkTicket";
+	}
+	
+	/**
+	 * 打开单点注销功能 
+	 */
+	public boolean isSlo = true;
+	
+	/**
+	 * 单点注销地址 
+	 */
+	public String sloUrl;
+
+	public String getSloUrl() {
+		return sloUrl = host + "/sso/signout";
+	}
+	/**
+	 * 接口调用秘钥 
+	 */
+	public String secretkey = "YQfyZtAmDbYHTBaHPSx3GZeX7x2ip7ik";
+	
+	/**
+	 * SSO-Server端 注销后跳转地址
+	 */
+	public String logoutRedirectUrl;
+
+	public String getLogoutRedirectUrl() {
+		return logoutRedirectUrl = host + "/home";
+	}
+    /**
+     * SSO-Server端 查询userinfo地址
+     */
+    public String userinfoUrl;
+	public String getUserinfoUrl() {
+		return userinfoUrl = host + "/sso/userinfo";
+	}
+	/**
+	 * 当前 client 的标识,可为 null
+	 */
+	public String client = "";
+
+
+	// -------------------------- 封装方法 
+	
+	/**
+     * 拼接 sso 授权地址
+     * @return
+     */
+    public String buildServerAuthUrl(String clientLoginUrl) {
+        String serverAuthUrl = getAuthUrl();
+        if(isNotEmpty(client)) {
+            serverAuthUrl = joinParam(serverAuthUrl, "client=" + client);
+        }
+        serverAuthUrl = joinParam(serverAuthUrl, "redirect=" + clientLoginUrl);
+        return serverAuthUrl;
+    }
+	
+	/**
+	 * 校验 ticket,返回 userId 
+	 * @param ticket ticket 码
+	 * @param request 请求对象 
+	 * @param currPath 当前接口的访问path 
+	 * @return
+	 */
+	public Object checkTicket(String ticket, HttpServletRequest request, String currPath) {
+		// 校验 ticket 的地址
+		String checkUrl = getCheckTicketUrl() + "?ticket=" + ticket;
+		
+		// 如果锁定 client 的标识 
+		if(isNotEmpty(client)) {
+			checkUrl += "&client=" + client;
+		}
+		// 如果打开了单点注销 
+		if(isSlo) {
+			String ssoLogoutCall = request.getRequestURL().toString().replace(currPath, "/sso/logoutCall");
+			checkUrl += "&ssoLogoutCall=" + ssoLogoutCall;
+		}
+		// 发起请求
+		AjaxJson result = request(checkUrl);
+		
+		// 200 代表校验成功 
+		if(result.getCode() == 200 && SsoRequestUtil.isEmpty(result.getData()) == false) {
+			// 登录上 
+			Object loginId = result.getData();
+			return loginId;
+		} else {
+			// 将 sso-server 回应的消息作为异常抛出 
+			throw new RuntimeException(result.getMsg());
+		}
+	}
+	
+	/**
+	 * 获取指定用户id的详细资料  (调用此接口的前提是 sso-server 端开放了 /sso/userinfo 路由)
+	 */
+	public SysLoginUser getUserInfo(Object loginId) {
+
+        // 组织 url 参数 
+		String timestamp = String.valueOf(System.currentTimeMillis());	// 时间戳 
+		String nonce = getRandomString(20);		// 随机字符串
+		String sign = getSign(loginId, timestamp, nonce, secretkey);	// 参数签名
+		
+		// 请求 
+        String url = getUserinfoUrl() +
+        		"?loginId=" + loginId +
+        		"&timestamp=" + timestamp +
+        		"&nonce=" + nonce +
+        		"&sign=" + sign;
+        AjaxJson result = request(url);
+        
+        // 如果返回值的 code 不是200,代表请求失败 
+        if(result.getCode() == null || result.getCode() != 200) {
+        	throw new RuntimeException(result.getMsg());
+        }
+        
+        // 解析出 user 
+        SysLoginUser user = JsonUtil.parseObjectToModel(result.getData(), SysLoginUser.class);
+        return user;
+	}
+	
+	
+	// -------------------------- 工具方法 
+	
+	/**
+	 * 发出请求,并返回 SaResult 结果 
+	 * @param url 请求地址 
+	 * @return 返回的结果 
+	 */
+	public static AjaxJson request(String url) {
+		System.out.println("------ 发起请求:" + url);
+		Map<String, Object> map = Forest.post(url).executeAsMap();
+		return new AjaxJson(map);
+	}
+
+	/**
+	 * 根据参数计算签名 
+	 * @param loginId 账号id
+	 * @param timestamp 当前时间戳,13位
+	 * @param nonce 随机字符串 
+	 * @param secretkey 账号id
+	 * @return 签名 
+	 */
+	public static String getSign(Object loginId, String timestamp, String nonce, String secretkey) {
+		return md5("loginId=" + loginId + "&nonce=" + nonce + "&timestamp=" + timestamp + "&key=" + secretkey);
+	}
+
+	/**
+	 * 指定元素是否为null或者空字符串
+	 * @param str 指定元素 
+	 * @return 是否为null或者空字符串
+	 */
+	public static boolean isEmpty(Object str) {
+		return str == null || "".equals(str);
+	}
+
+	/**
+	 * 指定元素是否不为null或者空字符串
+	 * @param str 指定元素 
+	 * @return 是否为null或者空字符串
+	 */
+	public static boolean isNotEmpty(Object str) {
+		return !isEmpty(str);
+	}
+	
+	/**
+	 * md5加密 
+	 * @param str 指定字符串
+	 * @return 加密后的字符串
+	 */
+	public static String md5(String str) {
+		str = (str == null ? "" : str);
+		char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+		try {
+			byte[] btInput = str.getBytes();
+			MessageDigest mdInst = MessageDigest.getInstance("MD5");
+			mdInst.update(btInput);
+			byte[] md = mdInst.digest();
+			int j = md.length;
+			char[] strA = new char[j * 2];
+			int k = 0;
+			for (byte byte0 : md) {
+				strA[k++] = hexDigits[byte0 >>> 4 & 0xf];
+				strA[k++] = hexDigits[byte0 & 0xf];
+			}
+			return new String(strA);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 生成指定长度的随机字符串
+	 * 
+	 * @param length 字符串的长度
+	 * @return 一个随机字符串
+	 */
+	public static String getRandomString(int length) {
+		String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+		Random random = new Random();
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < length; i++) {
+			int number = random.nextInt(62);
+			sb.append(str.charAt(number));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * URL编码 
+	 * @param url see note 
+	 * @return see note 
+	 */
+	public static String encodeUrl(String url) {
+		try {
+			return URLEncoder.encode(url, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 在url上拼接上kv参数并返回 
+	 * @param url url
+	 * @param parameStr 参数, 例如 id=1001
+	 * @return 拼接后的url字符串 
+	 */
+	public static String joinParam(String url, String parameStr) {
+		// 如果参数为空, 直接返回 
+		if(parameStr == null || parameStr.length() == 0) {
+			return url;
+		}
+		if(url == null) {
+			url = "";
+		}
+		int index = url.lastIndexOf('?');
+		// ? 不存在
+		if(index == -1) {
+			return url + '?' + parameStr;
+		}
+		// ? 是最后一位
+		if(index == url.length() - 1) {
+			return url + parameStr;
+		}
+		// ? 是其中一位
+		if(index > -1 && index < url.length() - 1) {
+			String separatorChar = "&";
+			// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
+			if(url.lastIndexOf(separatorChar) != url.length() - 1 && parameStr.indexOf(separatorChar) != 0) {
+				return url + separatorChar + parameStr;
+			} else {
+				return url + parameStr;
+			}
+		}
+		// 正常情况下, 代码不可能执行到此 
+		return url;
+	}
+}

+ 131 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/domain/SysLoginUser.java

@@ -0,0 +1,131 @@
+package com.songhua.framework.sso.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Model: sys_user -- 用户表
+ * @author shengzhang 
+ */
+@Data
+@Accessors(chain = true)
+public class SysLoginUser implements Serializable {
+
+	// ---------- 模块常量 ----------
+	/**
+	 * 序列化版本id 
+	 */
+	private static final long serialVersionUID = 1L;	
+	/**
+	 * 此模块对应的表名 
+	 */
+	public static final String TABLE_NAME = "sys_user";	
+	/**
+	 * 此模块对应的权限码 
+	 */
+	public static final String PERMISSION_CODE = "sys-user";	
+
+
+	// ---------- 表中字段 ----------
+	/**
+	 * id号 
+	 */
+	private Long id;	
+
+	/**
+	 * 用户昵称 
+	 */
+	private String username;	
+
+	/**
+	 * 账号密码 
+	 */
+	private String password;	
+
+	/**
+	 * 用户头像 
+	 */
+	private String avatar;	
+
+	/**
+	 * 个人介绍(签名) 
+	 */
+	private String intro;	
+
+	/**
+	 * 用户年龄 
+	 */
+	private Integer age;	
+
+	/**
+	 * 用户性别 (1=男,2=女,3=未知) 
+	 */
+	private Integer sex;	
+
+	/**
+	 * 手机号 
+	 */
+	private String phone;	
+
+	/**
+	 * 用户邮箱 
+	 */
+	private String email;	
+
+	/**
+	 * 账号状态(1=正常,2=禁用) 
+	 */
+	private Integer status;	
+
+	/**
+	 * 创建时间 
+	 */
+	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
+	private Date createTime;	
+
+	/**
+	 * 上次登陆时间 
+	 */
+	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
+	private Date loginTime;	
+
+	/**
+	 * 上次登陆IP 
+	 */
+	private String loginIp;	
+
+	/**
+	 * 登陆次数 
+	 */
+	private Integer loginCount;	
+
+	/**
+	 * 是否删除(1=否,2=是)
+	 */
+	private Integer isDel;	
+
+
+	/** 防止密码被传递到前台  */
+    public String getPassword(){
+    	return "********";
+    }
+    /** 获取真实密码   */
+    @JsonIgnore()
+    public String getPassword2(){
+    	return this.password;
+    }
+	
+
+
+	
+
+
+}

+ 231 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/utli/AjaxJson.java

@@ -0,0 +1,231 @@
+package com.songhua.framework.sso.utli;
+
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+
+/**
+ * ajax请求返回Json格式数据的封装 <br>
+ * 所有预留字段:<br>
+ * code=状态码 <br>
+ * msg=描述信息 <br>
+ * data=携带对象 <br>
+ * pageNo=当前页 <br>
+ * pageSize=页大小 <br>
+ * startIndex=起始索引 <br>
+ * dataCount=数据总数 <br>
+ * pageCount=分页总数 <br>
+ * <p> 返回范例:</p>
+ *  <pre>
+	{
+		"code": 200,    // 成功时=200, 失败时=500  msg=失败原因
+		"msg": "ok",
+		"data": {}
+	} 
+	</pre>
+ */
+public class AjaxJson extends LinkedHashMap<String, Object> implements Serializable{
+
+	private static final long serialVersionUID = 1L;	// 序列化版本号
+	
+	public static final int CODE_SUCCESS = 200;			// 成功状态码
+	public static final int CODE_ERROR = 500;			// 错误状态码
+	public static final int CODE_WARNING = 501;			// 警告状态码
+	public static final int CODE_NOT_JUR = 403;			// 无权限状态码
+	public static final int CODE_NOT_LOGIN = 401;		// 未登录状态码
+	public static final int CODE_INVALID_REQUEST = 400;	// 无效请求状态码
+
+	
+	
+	// ============================  写值取值  ================================== 
+	
+	/** 给code赋值,连缀风格 */
+	public AjaxJson setCode(int code) {
+		this.put("code", code);
+		return this;
+	}
+	/** 返回code */
+	public Integer getCode() {
+		return (Integer)this.get("code");
+	}
+
+	/** 给msg赋值,连缀风格 */
+	public AjaxJson setMsg(String msg) {
+		this.put("msg", msg);
+		return this;
+	}
+	/** 获取msg */
+	public String getMsg() {
+		return (String)this.get("msg");
+	}
+
+	/** 给data赋值,连缀风格 */
+	public AjaxJson setData(Object data) {
+		this.put("data", data);
+		return this;
+	}
+	/** 获取data */
+	public Object getData() {
+		return this.get("data");
+	}
+	/** 将data还原为指定类型并返回 */
+	@SuppressWarnings("unchecked")
+	public <T> T getData(Class<T> cs) {
+		return (T) this.getData();
+	}
+
+	/** 给dataCount(数据总数)赋值,连缀风格 */
+	public AjaxJson setDataCount(Long dataCount) {
+		this.put("dataCount", dataCount);
+		// 如果提供了数据总数,则尝试计算page信息
+		if(dataCount != null && dataCount >= 0) {		
+			// 如果:已有page信息
+			if(get("pageNo") != null) {
+				this.initPageInfo();
+			} 
+//			// 或者:是JavaWeb环境
+//			else if(SoMap.isJavaWeb()) {
+//				SoMap so = SoMap.getRequestSoMap();
+//				this.setPageNoAndSize(so.getKeyPageNo(), so.getKeyPageSize());
+//				this.initPageInfo();
+//			}
+		}
+		return this;
+	}
+	/** 获取dataCount(数据总数) */
+	public Long getDataCount() {
+		return (Long)this.get("dataCount");
+	}
+	
+	/** 设置pageNo 和 pageSize,并计算出startIndex于pageCount */
+	public AjaxJson setPageNoAndSize(long pageNo, long pageSize) {
+		this.put("pageNo", pageNo);
+		this.put("pageSize", pageSize);
+		return this;
+	}
+
+	/** 根据 pageSize dataCount,计算startIndex 与 pageCount */
+	public AjaxJson initPageInfo() {
+		long pageNo = (long)this.get("pageNo");
+		long pageSize = (long)this.get("pageSize");
+		long dataCount = (long)this.get("dataCount");
+		this.set("startIndex", (pageNo - 1) * pageSize);
+		long pc = dataCount / pageSize;
+		this.set("pageCount", (dataCount % pageSize == 0 ?  pc : pc + 1));
+		return this;
+	}
+	
+	
+	/** 写入一个值 自定义key, 连缀风格 */
+	public AjaxJson set(String key, Object data) {
+		this.put(key, data);
+		return this;
+	}
+
+	/** 写入一个Map, 连缀风格 */
+	public AjaxJson setMap(Map<String, ?> map) {
+		for (String key : map.keySet()) {
+			this.put(key, map.get(key));
+		}
+		return this;
+	}
+	
+	
+	// ============================  构建  ================================== 
+	
+	public AjaxJson(int code, String msg, Object data, Long dataCount) {
+		this.setCode(code);
+		this.setMsg(msg);
+		this.setData(data);
+		if(dataCount != null) {
+			this.setDataCount(dataCount);
+		}
+	}
+
+	public AjaxJson(Map<String, Object> map) {
+		for (String key: map.keySet()) {
+			this.set(key, map.get(key));
+		}
+	}
+	
+	/** 返回成功 */
+	public static AjaxJson getSuccess() {
+		return new AjaxJson(CODE_SUCCESS, "ok", null, null);
+	}
+	public static AjaxJson getSuccess(String msg) {
+		return new AjaxJson(CODE_SUCCESS, msg, null, null);
+	}
+	public static AjaxJson getSuccess(String msg, Object data) {
+		return new AjaxJson(CODE_SUCCESS, msg, data, null);
+	}
+	public static AjaxJson getSuccessData(Object data) {
+		return new AjaxJson(CODE_SUCCESS, "ok", data, null);
+	}
+	
+	
+	/** 返回失败 */
+	public static AjaxJson getError() {
+		return new AjaxJson(CODE_ERROR, "error", null, null);
+	}
+	public static AjaxJson getError(String msg) {
+		return new AjaxJson(CODE_ERROR, msg, null, null);
+	}
+	
+	/** 返回警告  */
+	public static AjaxJson getWarning() {
+		return new AjaxJson(CODE_ERROR, "warning", null, null);
+	}
+	public static AjaxJson getWarning(String msg) {
+		return new AjaxJson(CODE_WARNING, msg, null, null);
+	}
+
+	/** 返回未登录  */
+	public static AjaxJson getNotLogin() {
+		return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
+	}
+
+	/** 返回没有权限的  */
+	public static AjaxJson getNotJur(String msg) {
+		return new AjaxJson(CODE_NOT_JUR, msg, null, null);
+	}
+	
+	/** 返回一个自定义状态码的  */
+	public static AjaxJson get(int code, String msg){
+		return new AjaxJson(code, msg, null, null);
+	}
+	
+	/** 返回分页和数据的  */
+	public static AjaxJson getPageData(Long dataCount, Object data){
+		return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
+	}
+	
+	/** 返回, 根据受影响行数的(大于0=ok,小于0=error)  */
+	public static AjaxJson getByLine(int line){
+		if(line > 0){
+			return getSuccess("ok", line);
+		}
+		return getError("error").setData(line); 
+	}
+
+	/** 返回,根据布尔值来确定最终结果的  (true=ok,false=error)  */
+	public static AjaxJson getByBoolean(boolean b){
+		return b ? getSuccess("ok") : getError("error"); 
+	}
+	
+	
+	
+	
+	
+	
+	
+//  // 历史版本遗留代码 
+//	public int code; 	// 状态码
+//	public String msg; 	// 描述信息 
+//	public Object data; // 携带对象
+//	public Long dataCount;	// 数据总数,用于分页 
+
+	
+	
+	
+}

+ 63 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/utli/JsonUtil.java

@@ -0,0 +1,63 @@
+package com.songhua.framework.sso.utli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.Map;
+
+/**
+ *  JSON 转换器
+ * 
+ * @author kong
+ * @since: 2022-11-24
+ */
+public class JsonUtil {
+
+	/**
+	 * 底层 Mapper 对象 
+	 */
+	public static ObjectMapper objectMapper = new ObjectMapper();
+
+	/**
+	 * 将任意对象转换为 json 字符串 
+	 * 
+	 * @param obj 对象 
+	 * @return 转换后的 json 字符串
+	 */
+	public static String toJsonString(Object obj) {
+		try {
+			return objectMapper.writeValueAsString(obj);
+		} catch (JsonProcessingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	/**
+	 * 将 json 字符串解析为 Map
+	 */
+	public static Map<String, Object> parseJsonToMap(String jsonStr) {
+		try {
+			@SuppressWarnings("unchecked")
+			Map<String, Object> map = objectMapper.readValue(jsonStr, Map.class);
+			return map;
+		} catch (JsonProcessingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * 将任意类型转化为指定类型 
+	 */
+	public static <T>T parseObjectToModel(Object obj, Class<T> cs) {
+		if(obj == null) {
+			return null;
+		}
+		String jsonStr = toJsonString(obj);
+		try {
+			return objectMapper.readValue(jsonStr, cs);
+		} catch (JsonProcessingException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}

+ 65 - 0
songhua-framework/src/main/java/com/songhua/framework/sso/utli/MyHttpSessionHolder.java

@@ -0,0 +1,65 @@
+package com.songhua.framework.sso.utli;
+
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 记录所有已创建的 HttpSession 对象 
+ * 
+ * <b> 此种方式有性能问题,仅做demo示例,真实项目中请更换为其它方案记录用户会话数据 </b>
+ * 
+ * @author kong
+ * @since: 2022-4-30
+ */
+@Component
+public class MyHttpSessionHolder implements HttpSessionListener {
+	
+	public static Map<String, HttpSession> sessionMap = new LinkedHashMap<String, HttpSession>();
+	
+	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
+		HttpSession session = httpSessionEvent.getSession();
+		sessionMap.put(session.getId(), session);
+	}
+
+	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
+		HttpSession session = httpSessionEvent.getSession();
+		sessionMap.remove(session.getId());
+	}
+
+	/**
+	 * 获取指定请求的 HttpSession (前后台分离模式下必须使用此方法获取 HttpSession ) 
+	 * @param request
+	 */
+	public static HttpSession getSession(HttpServletRequest request) {
+		// 先使用默认的尝试一下 
+		HttpSession session = request.getSession(false);
+		if(session != null) {
+			return session;
+		}
+		
+		// 获取前端提交的 token
+		String token = request.getHeader("JSESSIONID");
+		if(token == null) {
+			token = request.getHeader("satoken");
+			if(token == null) {
+				token = request.getParameter("satoken");
+			}
+		}
+		
+		// 获取
+		HttpSession session2 = sessionMap.get(token);
+		if(session2 != null) {
+			return session2;
+		}
+		
+		return request.getSession();
+	}
+
+	
+}

+ 59 - 57
songhua-framework/src/main/java/com/songhua/framework/web/service/SysLoginService.java

@@ -1,12 +1,5 @@
 package com.songhua.framework.web.service;
 
-import javax.annotation.Resource;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.stereotype.Component;
 import com.songhua.common.constant.CacheConstants;
 import com.songhua.common.constant.Constants;
 import com.songhua.common.constant.UserConstants;
@@ -14,11 +7,7 @@ import com.songhua.common.core.domain.entity.SysUser;
 import com.songhua.common.core.domain.model.LoginUser;
 import com.songhua.common.core.redis.RedisCache;
 import com.songhua.common.exception.ServiceException;
-import com.songhua.common.exception.user.BlackListException;
-import com.songhua.common.exception.user.CaptchaException;
-import com.songhua.common.exception.user.CaptchaExpireException;
-import com.songhua.common.exception.user.UserNotExistsException;
-import com.songhua.common.exception.user.UserPasswordNotMatchException;
+import com.songhua.common.exception.user.*;
 import com.songhua.common.utils.DateUtils;
 import com.songhua.common.utils.MessageUtils;
 import com.songhua.common.utils.StringUtils;
@@ -26,17 +15,25 @@ import com.songhua.common.utils.ip.IpUtils;
 import com.songhua.framework.manager.AsyncManager;
 import com.songhua.framework.manager.factory.AsyncFactory;
 import com.songhua.framework.security.context.AuthenticationContextHolder;
+import com.songhua.system.mapper.SysUserMapper;
 import com.songhua.system.service.ISysConfigService;
 import com.songhua.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
 
 /**
  * 登录校验方法
- * 
+ *
  * @author ruoyi
  */
 @Component
-public class SysLoginService
-{
+public class SysLoginService {
     @Autowired
     private TokenService tokenService;
 
@@ -45,52 +42,46 @@ public class SysLoginService
 
     @Autowired
     private RedisCache redisCache;
-    
+
     @Autowired
     private ISysUserService userService;
 
     @Autowired
     private ISysConfigService configService;
 
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
     /**
      * 登录验证
-     * 
+     *
      * @param username 用户名
      * @param password 密码
-     * @param code 验证码
-     * @param uuid 唯一标识
+     * @param code     验证码
+     * @param uuid     唯一标识
      * @return 结果
      */
-    public String login(String username, String password, String code, String uuid)
-    {
+    public String login(String username, String password, String code, String uuid) {
         // 验证码校验
         validateCaptcha(username, code, uuid);
         // 登录前置校验
         loginPreCheck(username, password);
         // 用户验证
         Authentication authentication = null;
-        try
-        {
+        try {
             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
             AuthenticationContextHolder.setContext(authenticationToken);
             // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
             authentication = authenticationManager.authenticate(authenticationToken);
-        }
-        catch (Exception e)
-        {
-            if (e instanceof BadCredentialsException)
-            {
+        } catch (Exception e) {
+            if (e instanceof BadCredentialsException) {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                 throw new UserPasswordNotMatchException();
-            }
-            else
-            {
+            } else {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                 throw new ServiceException(e.getMessage());
             }
-        }
-        finally
-        {
+        } finally {
             AuthenticationContextHolder.clearContext();
         }
         AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
@@ -100,29 +91,45 @@ public class SysLoginService
         return tokenService.createToken(loginUser);
     }
 
+
+    /**
+     * 登录验证
+     *
+     * @param username 用户名
+     * @return 结果
+     */
+    public String ssoLogin(String username) {
+        SysUser sysUser = sysUserMapper.selectUserByUserName(username);
+        LoginUser loginUser = new LoginUser();
+        loginUser.setUserId(sysUser.getUserId());
+        loginUser.setDeptId(sysUser.getDeptId());
+        loginUser.setUser(sysUser);
+        // 生成token
+        return tokenService.createToken(loginUser);
+    }
+
+
+
+
     /**
      * 校验验证码
-     * 
+     *
      * @param username 用户名
-     * @param code 验证码
-     * @param uuid 唯一标识
+     * @param code     验证码
+     * @param uuid     唯一标识
      * @return 结果
      */
-    public void validateCaptcha(String username, String code, String uuid)
-    {
+    public void validateCaptcha(String username, String code, String uuid) {
         boolean captchaEnabled = configService.selectCaptchaEnabled();
-        if (captchaEnabled)
-        {
+        if (captchaEnabled) {
             String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
             String captcha = redisCache.getCacheObject(verifyKey);
             redisCache.deleteObject(verifyKey);
-            if (captcha == null)
-            {
+            if (captcha == null) {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                 throw new CaptchaExpireException();
             }
-            if (!code.equalsIgnoreCase(captcha))
-            {
+            if (!code.equalsIgnoreCase(captcha)) {
                 AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                 throw new CaptchaException();
             }
@@ -131,35 +138,31 @@ public class SysLoginService
 
     /**
      * 登录前置校验
+     *
      * @param username 用户名
      * @param password 用户密码
      */
-    public void loginPreCheck(String username, String password)
-    {
+    public void loginPreCheck(String username, String password) {
         // 用户名或密码为空 错误
-        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
-        {
+        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
             throw new UserNotExistsException();
         }
         // 密码如果不在指定范围内 错误
         if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
-                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
-        {
+                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
         // 用户名不在指定范围内 错误
         if (username.length() < UserConstants.USERNAME_MIN_LENGTH
-                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
-        {
+                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
             throw new UserPasswordNotMatchException();
         }
         // IP黑名单校验
         String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
-        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
-        {
+        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
             throw new BlackListException();
         }
@@ -170,8 +173,7 @@ public class SysLoginService
      *
      * @param userId 用户ID
      */
-    public void recordLoginInfo(Long userId)
-    {
+    public void recordLoginInfo(Long userId) {
         SysUser sysUser = new SysUser();
         sysUser.setUserId(userId);
         sysUser.setLoginIp(IpUtils.getIpAddr());

+ 30 - 50
songhua-framework/src/main/java/com/songhua/framework/web/service/TokenService.java

@@ -1,14 +1,5 @@
 package com.songhua.framework.web.service;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.http.HttpServletRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
 import com.songhua.common.constant.CacheConstants;
 import com.songhua.common.constant.Constants;
 import com.songhua.common.core.domain.model.LoginUser;
@@ -22,6 +13,16 @@ import eu.bitwalker.useragentutils.UserAgent;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * token验证处理
@@ -29,8 +30,7 @@ import io.jsonwebtoken.SignatureAlgorithm;
  * @author ruoyi
  */
 @Component
-public class TokenService
-{
+public class TokenService {
     private static final Logger log = LoggerFactory.getLogger(TokenService.class);
 
     // 令牌自定义标识
@@ -59,23 +59,18 @@ public class TokenService
      *
      * @return 用户信息
      */
-    public LoginUser getLoginUser(HttpServletRequest request)
-    {
+    public LoginUser getLoginUser(HttpServletRequest request) {
         // 获取请求携带的令牌
         String token = getToken(request);
-        if (StringUtils.isNotEmpty(token))
-        {
-            try
-            {
+        if (StringUtils.isNotEmpty(token)) {
+            try {
                 Claims claims = parseToken(token);
                 // 解析对应的权限以及用户信息
                 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                 String userKey = getTokenKey(uuid);
                 LoginUser user = redisCache.getCacheObject(userKey);
                 return user;
-            }
-            catch (Exception e)
-            {
+            } catch (Exception e) {
                 log.error("获取用户信息异常'{}'", e.getMessage());
             }
         }
@@ -85,10 +80,8 @@ public class TokenService
     /**
      * 设置用户身份信息
      */
-    public void setLoginUser(LoginUser loginUser)
-    {
-        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
-        {
+    public void setLoginUser(LoginUser loginUser) {
+        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
             refreshToken(loginUser);
         }
     }
@@ -96,10 +89,8 @@ public class TokenService
     /**
      * 删除用户身份信息
      */
-    public void delLoginUser(String token)
-    {
-        if (StringUtils.isNotEmpty(token))
-        {
+    public void delLoginUser(String token) {
+        if (StringUtils.isNotEmpty(token)) {
             String userKey = getTokenKey(token);
             redisCache.deleteObject(userKey);
         }
@@ -111,8 +102,7 @@ public class TokenService
      * @param loginUser 用户信息
      * @return 令牌
      */
-    public String createToken(LoginUser loginUser)
-    {
+    public String createToken(LoginUser loginUser) {
         String token = IdUtils.fastUUID();
         loginUser.setToken(token);
         setUserAgent(loginUser);
@@ -129,12 +119,10 @@ public class TokenService
      * @param loginUser
      * @return 令牌
      */
-    public void verifyToken(LoginUser loginUser)
-    {
+    public void verifyToken(LoginUser loginUser) {
         long expireTime = loginUser.getExpireTime();
         long currentTime = System.currentTimeMillis();
-        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
-        {
+        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
             refreshToken(loginUser);
         }
     }
@@ -144,8 +132,7 @@ public class TokenService
      *
      * @param loginUser 登录信息
      */
-    public void refreshToken(LoginUser loginUser)
-    {
+    public void refreshToken(LoginUser loginUser) {
         loginUser.setLoginTime(System.currentTimeMillis());
         loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
         // 根据uuid将loginUser缓存
@@ -158,8 +145,7 @@ public class TokenService
      *
      * @param loginUser 登录信息
      */
-    public void setUserAgent(LoginUser loginUser)
-    {
+    public void setUserAgent(LoginUser loginUser) {
         UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
         String ip = IpUtils.getIpAddr();
         loginUser.setIpaddr(ip);
@@ -174,8 +160,7 @@ public class TokenService
      * @param claims 数据声明
      * @return 令牌
      */
-    private String createToken(Map<String, Object> claims)
-    {
+    private String createToken(Map<String, Object> claims) {
         String token = Jwts.builder()
                 .setClaims(claims)
                 .signWith(SignatureAlgorithm.HS512, secret).compact();
@@ -188,8 +173,7 @@ public class TokenService
      * @param token 令牌
      * @return 数据声明
      */
-    private Claims parseToken(String token)
-    {
+    private Claims parseToken(String token) {
         return Jwts.parser()
                 .setSigningKey(secret)
                 .parseClaimsJws(token)
@@ -202,8 +186,7 @@ public class TokenService
      * @param token 令牌
      * @return 用户名
      */
-    public String getUsernameFromToken(String token)
-    {
+    public String getUsernameFromToken(String token) {
         Claims claims = parseToken(token);
         return claims.getSubject();
     }
@@ -214,18 +197,15 @@ public class TokenService
      * @param request
      * @return token
      */
-    private String getToken(HttpServletRequest request)
-    {
+    private String getToken(HttpServletRequest request) {
         String token = request.getHeader(header);
-        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
-        {
+        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
             token = token.replace(Constants.TOKEN_PREFIX, "");
         }
         return token;
     }
 
-    private String getTokenKey(String uuid)
-    {
+    private String getTokenKey(String uuid) {
         return CacheConstants.LOGIN_TOKEN_KEY + uuid;
     }
 }

+ 82 - 135
songhua-system/src/main/java/com/songhua/system/service/impl/SysUserServiceImpl.java

@@ -4,6 +4,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import javax.validation.Validator;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -32,12 +33,11 @@ import com.songhua.system.service.ISysUserService;
 
 /**
  * 用户 业务层处理
- * 
+ *
  * @author ruoyi
  */
 @Service
-public class SysUserServiceImpl implements ISysUserService
-{
+public class SysUserServiceImpl implements ISysUserService {
     private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
 
     @Autowired
@@ -63,79 +63,72 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 根据条件分页查询用户列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
     @Override
     @DataScope(deptAlias = "d", userAlias = "u")
-    public List<SysUser> selectUserList(SysUser user)
-    {
+    public List<SysUser> selectUserList(SysUser user) {
         return userMapper.selectUserList(user);
     }
 
     /**
      * 根据条件分页查询已分配用户角色列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
     @Override
     @DataScope(deptAlias = "d", userAlias = "u")
-    public List<SysUser> selectAllocatedList(SysUser user)
-    {
+    public List<SysUser> selectAllocatedList(SysUser user) {
         return userMapper.selectAllocatedList(user);
     }
 
     /**
      * 根据条件分页查询未分配用户角色列表
-     * 
+     *
      * @param user 用户信息
      * @return 用户信息集合信息
      */
     @Override
     @DataScope(deptAlias = "d", userAlias = "u")
-    public List<SysUser> selectUnallocatedList(SysUser user)
-    {
+    public List<SysUser> selectUnallocatedList(SysUser user) {
         return userMapper.selectUnallocatedList(user);
     }
 
     /**
      * 通过用户名查询用户
-     * 
+     *
      * @param userName 用户名
      * @return 用户对象信息
      */
     @Override
-    public SysUser selectUserByUserName(String userName)
-    {
+    public SysUser selectUserByUserName(String userName) {
         return userMapper.selectUserByUserName(userName);
     }
 
     /**
      * 通过用户ID查询用户
-     * 
+     *
      * @param userId 用户ID
      * @return 用户对象信息
      */
     @Override
-    public SysUser selectUserById(Long userId)
-    {
+    public SysUser selectUserById(Long userId) {
         return userMapper.selectUserById(userId);
     }
 
     /**
      * 查询用户所属角色组
-     * 
+     *
      * @param userName 用户名
      * @return 结果
      */
     @Override
-    public String selectUserRoleGroup(String userName)
-    {
+    public String selectUserRoleGroup(String userName) {
         List<SysRole> list = roleMapper.selectRolesByUserName(userName);
-        if (CollectionUtils.isEmpty(list))
-        {
+        if (CollectionUtils.isEmpty(list)) {
             return StringUtils.EMPTY;
         }
         return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(","));
@@ -143,16 +136,14 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 查询用户所属岗位组
-     * 
+     *
      * @param userName 用户名
      * @return 结果
      */
     @Override
-    public String selectUserPostGroup(String userName)
-    {
+    public String selectUserPostGroup(String userName) {
         List<SysPost> list = postMapper.selectPostsByUserName(userName);
-        if (CollectionUtils.isEmpty(list))
-        {
+        if (CollectionUtils.isEmpty(list)) {
             return StringUtils.EMPTY;
         }
         return list.stream().map(SysPost::getPostName).collect(Collectors.joining(","));
@@ -160,17 +151,15 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 校验用户名称是否唯一
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
-    public boolean checkUserNameUnique(SysUser user)
-    {
+    public boolean checkUserNameUnique(SysUser user) {
         Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
         SysUser info = userMapper.checkUserNameUnique(user.getUserName());
-        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
-        {
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
             return UserConstants.NOT_UNIQUE;
         }
         return UserConstants.UNIQUE;
@@ -183,12 +172,10 @@ public class SysUserServiceImpl implements ISysUserService
      * @return
      */
     @Override
-    public boolean checkPhoneUnique(SysUser user)
-    {
+    public boolean checkPhoneUnique(SysUser user) {
         Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
         SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber());
-        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
-        {
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
             return UserConstants.NOT_UNIQUE;
         }
         return UserConstants.UNIQUE;
@@ -201,12 +188,10 @@ public class SysUserServiceImpl implements ISysUserService
      * @return
      */
     @Override
-    public boolean checkEmailUnique(SysUser user)
-    {
+    public boolean checkEmailUnique(SysUser user) {
         Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId();
         SysUser info = userMapper.checkEmailUnique(user.getEmail());
-        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue())
-        {
+        if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) {
             return UserConstants.NOT_UNIQUE;
         }
         return UserConstants.UNIQUE;
@@ -214,33 +199,28 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 校验用户是否允许操作
-     * 
+     *
      * @param user 用户信息
      */
     @Override
-    public void checkUserAllowed(SysUser user)
-    {
-        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin())
-        {
+    public void checkUserAllowed(SysUser user) {
+        if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) {
             throw new ServiceException("不允许操作超级管理员用户");
         }
     }
 
     /**
      * 校验用户是否有数据权限
-     * 
+     *
      * @param userId 用户id
      */
     @Override
-    public void checkUserDataScope(Long userId)
-    {
-        if (!SysUser.isAdmin(SecurityUtils.getUserId()))
-        {
+    public void checkUserDataScope(Long userId) {
+        if (!SysUser.isAdmin(SecurityUtils.getUserId())) {
             SysUser user = new SysUser();
             user.setUserId(userId);
             List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user);
-            if (StringUtils.isEmpty(users))
-            {
+            if (StringUtils.isEmpty(users)) {
                 throw new ServiceException("没有权限访问用户数据!");
             }
         }
@@ -248,14 +228,13 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 新增保存用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
     @Transactional
-    public int insertUser(SysUser user)
-    {
+    public int insertUser(SysUser user) {
         // 新增用户信息
         int rows = userMapper.insertUser(user);
         // 新增用户岗位关联
@@ -267,26 +246,24 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 注册用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
-    public boolean registerUser(SysUser user)
-    {
+    public boolean registerUser(SysUser user) {
         return userMapper.insertUser(user) > 0;
     }
 
     /**
      * 修改保存用户信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
     @Transactional
-    public int updateUser(SysUser user)
-    {
+    public int updateUser(SysUser user) {
         Long userId = user.getUserId();
         // 删除用户与角色关联
         userRoleMapper.deleteUserRoleByUserId(userId);
@@ -301,104 +278,94 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 用户授权角色
-     * 
-     * @param userId 用户ID
+     *
+     * @param userId  用户ID
      * @param roleIds 角色组
      */
     @Override
     @Transactional
-    public void insertUserAuth(Long userId, Long[] roleIds)
-    {
+    public void insertUserAuth(Long userId, Long[] roleIds) {
         userRoleMapper.deleteUserRoleByUserId(userId);
         insertUserRole(userId, roleIds);
     }
 
     /**
      * 修改用户状态
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
-    public int updateUserStatus(SysUser user)
-    {
+    public int updateUserStatus(SysUser user) {
         return userMapper.updateUser(user);
     }
 
     /**
      * 修改用户基本信息
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
-    public int updateUserProfile(SysUser user)
-    {
+    public int updateUserProfile(SysUser user) {
         return userMapper.updateUser(user);
     }
 
     /**
      * 修改用户头像
-     * 
+     *
      * @param userName 用户名
-     * @param avatar 头像地址
+     * @param avatar   头像地址
      * @return 结果
      */
     @Override
-    public boolean updateUserAvatar(String userName, String avatar)
-    {
+    public boolean updateUserAvatar(String userName, String avatar) {
         return userMapper.updateUserAvatar(userName, avatar) > 0;
     }
 
     /**
      * 重置用户密码
-     * 
+     *
      * @param user 用户信息
      * @return 结果
      */
     @Override
-    public int resetPwd(SysUser user)
-    {
+    public int resetPwd(SysUser user) {
         return userMapper.updateUser(user);
     }
 
     /**
      * 重置用户密码
-     * 
+     *
      * @param userName 用户名
      * @param password 密码
      * @return 结果
      */
     @Override
-    public int resetUserPwd(String userName, String password)
-    {
+    public int resetUserPwd(String userName, String password) {
         return userMapper.resetUserPwd(userName, password);
     }
 
     /**
      * 新增用户角色信息
-     * 
+     *
      * @param user 用户对象
      */
-    public void insertUserRole(SysUser user)
-    {
+    public void insertUserRole(SysUser user) {
         this.insertUserRole(user.getUserId(), user.getRoleIds());
     }
 
     /**
      * 新增用户岗位信息
-     * 
+     *
      * @param user 用户对象
      */
-    public void insertUserPost(SysUser user)
-    {
+    public void insertUserPost(SysUser user) {
         Long[] posts = user.getPostIds();
-        if (StringUtils.isNotEmpty(posts))
-        {
+        if (StringUtils.isNotEmpty(posts)) {
             // 新增用户与岗位管理
             List<SysUserPost> list = new ArrayList<SysUserPost>(posts.length);
-            for (Long postId : posts)
-            {
+            for (Long postId : posts) {
                 SysUserPost up = new SysUserPost();
                 up.setUserId(user.getUserId());
                 up.setPostId(postId);
@@ -410,18 +377,15 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 新增用户角色信息
-     * 
-     * @param userId 用户ID
+     *
+     * @param userId  用户ID
      * @param roleIds 角色组
      */
-    public void insertUserRole(Long userId, Long[] roleIds)
-    {
-        if (StringUtils.isNotEmpty(roleIds))
-        {
+    public void insertUserRole(Long userId, Long[] roleIds) {
+        if (StringUtils.isNotEmpty(roleIds)) {
             // 新增用户与角色管理
             List<SysUserRole> list = new ArrayList<SysUserRole>(roleIds.length);
-            for (Long roleId : roleIds)
-            {
+            for (Long roleId : roleIds) {
                 SysUserRole ur = new SysUserRole();
                 ur.setUserId(userId);
                 ur.setRoleId(roleId);
@@ -433,14 +397,13 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 通过用户ID删除用户
-     * 
+     *
      * @param userId 用户ID
      * @return 结果
      */
     @Override
     @Transactional
-    public int deleteUserById(Long userId)
-    {
+    public int deleteUserById(Long userId) {
         // 删除用户与角色关联
         userRoleMapper.deleteUserRoleByUserId(userId);
         // 删除用户与岗位表
@@ -450,16 +413,14 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 批量删除用户信息
-     * 
+     *
      * @param userIds 需要删除的用户ID
      * @return 结果
      */
     @Override
     @Transactional
-    public int deleteUserByIds(Long[] userIds)
-    {
-        for (Long userId : userIds)
-        {
+    public int deleteUserByIds(Long[] userIds) {
+        for (Long userId : userIds) {
             checkUserAllowed(new SysUser(userId));
             checkUserDataScope(userId);
         }
@@ -472,17 +433,15 @@ public class SysUserServiceImpl implements ISysUserService
 
     /**
      * 导入用户数据
-     * 
-     * @param userList 用户数据列表
+     *
+     * @param userList        用户数据列表
      * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
-     * @param operName 操作用户
+     * @param operName        操作用户
      * @return 结果
      */
     @Override
-    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
-    {
-        if (StringUtils.isNull(userList) || userList.size() == 0)
-        {
+    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName) {
+        if (StringUtils.isNull(userList) || userList.size() == 0) {
             throw new ServiceException("导入用户数据不能为空!");
         }
         int successNum = 0;
@@ -490,23 +449,18 @@ public class SysUserServiceImpl implements ISysUserService
         StringBuilder successMsg = new StringBuilder();
         StringBuilder failureMsg = new StringBuilder();
         String password = configService.selectConfigByKey("sys.user.initPassword");
-        for (SysUser user : userList)
-        {
-            try
-            {
+        for (SysUser user : userList) {
+            try {
                 // 验证是否存在这个用户
                 SysUser u = userMapper.selectUserByUserName(user.getUserName());
-                if (StringUtils.isNull(u))
-                {
+                if (StringUtils.isNull(u)) {
                     BeanValidators.validateWithException(validator, user);
                     user.setPassword(SecurityUtils.encryptPassword(password));
                     user.setCreateBy(operName);
                     userMapper.insertUser(user);
                     successNum++;
                     successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
-                }
-                else if (isUpdateSupport)
-                {
+                } else if (isUpdateSupport) {
                     BeanValidators.validateWithException(validator, user);
                     checkUserAllowed(u);
                     checkUserDataScope(u.getUserId());
@@ -515,28 +469,21 @@ public class SysUserServiceImpl implements ISysUserService
                     userMapper.updateUser(user);
                     successNum++;
                     successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功");
-                }
-                else
-                {
+                } else {
                     failureNum++;
                     failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在");
                 }
-            }
-            catch (Exception e)
-            {
+            } catch (Exception e) {
                 failureNum++;
                 String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:";
                 failureMsg.append(msg + e.getMessage());
                 log.error(msg, e);
             }
         }
-        if (failureNum > 0)
-        {
+        if (failureNum > 0) {
             failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
             throw new ServiceException(failureMsg.toString());
-        }
-        else
-        {
+        } else {
             successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
         }
         return successMsg.toString();

+ 1 - 1
songhua-ui/package.json

@@ -37,7 +37,7 @@
   },
   "dependencies": {
     "@riophae/vue-treeselect": "0.4.0",
-    "axios": "0.24.0",
+    "axios": "^1.1.3",
     "clipboard": "2.0.8",
     "core-js": "3.25.3",
     "echarts": "5.4.0",

+ 39 - 0
songhua-ui/src/api/sso/method-util.js

@@ -0,0 +1,39 @@
+import axios from 'axios'
+
+// sso-client 的后端服务地址
+export const baseUrl = "http://127.0.0.1:8080";
+// export const baseUrl = process.env.VUE_APP_BASE_API;
+
+
+// 封装一下 Ajax 方法
+export const ajax = function(path, data, successFn) {
+    axios({
+        url: baseUrl + path,
+        method: 'post',
+        data: data,
+        headers: {
+            "Content-Type": "application/x-www-form-urlencoded",
+            "satoken": localStorage.getItem("satoken")
+        }
+    }).
+    then(function (response) { // 成功时执行
+        const res = response.data;
+        successFn(res);
+    }).
+    catch(function (error) {
+        alert("异常:" + JSON.stringify(error));
+        throw error;
+    })
+}
+
+// 从url中查询到指定名称的参数值
+export const getParam = function(name, defaultValue){
+    var query = window.location.search.substring(1);
+    var vars = query.split("&");
+    for (var i=0;i<vars.length;i++) {
+        var pair = vars[i].split("=");
+        if(pair[0] == name){return pair[1];}
+    }
+    return(defaultValue == undefined ? null : defaultValue);
+}
+

+ 10 - 2
songhua-ui/src/layout/components/Navbar.vue

@@ -56,7 +56,7 @@ import SizeSelect from '@/components/SizeSelect'
 import Search from '@/components/HeaderSearch'
 import RuoYiGit from '@/components/RuoYi/Git'
 import RuoYiDoc from '@/components/RuoYi/Doc'
-
+import {ajax, getParam} from '@/api/sso/method-util';
 export default {
   components: {
     Breadcrumb,
@@ -102,7 +102,15 @@ export default {
         type: 'warning'
       }).then(() => {
         this.$store.dispatch('LogOut').then(() => {
-          location.href = '/index';
+          // location.href = '/index';
+          ajax('/sso/logout', {}, function(res) {
+            // 根据返回的状态码执行不同动作
+            if(res.code === 200) {
+              location.href = res.data;
+            } else {
+              alert(res.msg);
+            }
+          });
         })
       }).catch(() => {});
     }

+ 13 - 12
songhua-ui/src/permission.js

@@ -1,14 +1,14 @@
 import router from './router'
 import store from './store'
-import { Message } from 'element-ui'
+import {Message} from 'element-ui'
 import NProgress from 'nprogress'
 import 'nprogress/nprogress.css'
-import { getToken } from '@/utils/auth'
-import { isRelogin } from '@/utils/request'
+import {getToken} from '@/utils/auth'
+import {isRelogin} from '@/utils/request'
 
-NProgress.configure({ showSpinner: false })
+NProgress.configure({showSpinner: false})
 
-const whiteList = ['/login', '/register']
+const whiteList = ['/login', '/register', '/sso-login']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()
@@ -16,7 +16,7 @@ router.beforeEach((to, from, next) => {
     to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
     /* has token*/
     if (to.path === '/login') {
-      next({ path: '/' })
+      next({path: '/'})
       NProgress.done()
     } else if (whiteList.indexOf(to.path) !== -1) {
       next()
@@ -29,14 +29,14 @@ router.beforeEach((to, from, next) => {
           store.dispatch('GenerateRoutes').then(accessRoutes => {
             // 根据roles权限生成可访问的路由表
             router.addRoutes(accessRoutes) // 动态添加可访问路由表
-            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
+            next({...to, replace: true}) // hack方法 确保addRoutes已完成
           })
         }).catch(err => {
-            store.dispatch('LogOut').then(() => {
-              Message.error(err)
-              next({ path: '/' })
-            })
+          store.dispatch('LogOut').then(() => {
+            Message.error(err)
+            next({path: '/'})
           })
+        })
       } else {
         next()
       }
@@ -47,7 +47,8 @@ router.beforeEach((to, from, next) => {
       // 在免登录白名单,直接进入
       next()
     } else {
-      next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页
+      // next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页
+      next(`/sso-login`) // 否则全部重定向到单点登录的登录页,而不是再进入原来的登录页
       NProgress.done()
     }
   }

+ 6 - 0
songhua-ui/src/router/index.js

@@ -61,6 +61,12 @@ export const constantRoutes = [
     component: () => import('@/views/error/401'),
     hidden: true
   },
+  // SSO-登录用
+  {
+    name: 'sso-login',
+    path: '/sso-login',
+    component: () => import('@/views/sso/sso-login'),
+  },
   {
     path: '',
     component: Layout,

+ 6 - 0
songhua-ui/src/utils/auth.js

@@ -2,10 +2,16 @@ import Cookies from 'js-cookie'
 
 const TokenKey = 'Admin-Token'
 
+const ExpiresInKey = 'Admin-Expires-In'
+
 export function getToken() {
   return Cookies.get(TokenKey)
 }
 
+export function setExpiresIn(time) {
+  return Cookies.set(ExpiresInKey, time)
+}
+
 export function setToken(token) {
   return Cookies.set(TokenKey, token)
 }

+ 53 - 0
songhua-ui/src/views/sso/sso-login.vue

@@ -0,0 +1,53 @@
+<!-- Client端-登录页 -->
+<template>
+  <div></div>
+</template>
+
+<script>
+import {getToken, setToken, setExpiresIn, removeToken} from '@/utils/auth';
+import {ajax, getParam} from '../../api/sso/method-util';
+import router from '@/router';
+
+export default {
+  name: 'App',
+  data() {
+    return {
+      back: getParam('back') || router.currentRoute.query.back,
+      ticket: getParam('ticket') || router.currentRoute.query.ticket
+    }
+  },
+  // 页面加载后触发
+  created() {
+    console.log('获取 back 参数:', this.back)
+    console.log('获取 ticket 参数:', this.ticket)
+    if (this.ticket) {
+      this.doLoginByTicket(this.ticket);
+    } else {
+      this.goSsoAuthUrl();
+    }
+  },
+  methods: {
+    // 重定向至认证中心
+    goSsoAuthUrl() {
+      ajax('/sso/getSsoAuthUrl', {clientLoginUrl: location.href}, function (res) {
+        console.log('/sso/getSsoAuthUrl 返回数据', res);
+        location.href = res.data;
+      })
+    },
+    // 根据ticket值登录
+    doLoginByTicket(ticket) {
+      ajax('/sso/doLoginByTicket', {ticket: ticket}, function (res) {
+        console.log("resss",res)
+        if (res.code === 200) {
+          localStorage.setItem('satoken', res.data.session_id);
+          setToken(res.data.access_token);
+          setExpiresIn(res.data.expires_in);
+          location.href = decodeURIComponent(this.back);
+        } else {
+          alert(res.msg);
+        }
+      }.bind(this))
+    }
+  }
+}
+</script>