diff --git a/.gitignore b/.gitignore
index 3d0d1fa..67271a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,7 @@ hs_err_pid*
/dist/
/nbdist/
/.nb-gradle/
-target
\ No newline at end of file
+target
+.mvn
+mvnw
+mvnw.cmd
\ No newline at end of file
diff --git a/1.SSO单点登录/sso/.gitignore b/1.SSO单点登录/sso/.gitignore
new file mode 100644
index 0000000..ba5cb5c
--- /dev/null
+++ b/1.SSO单点登录/sso/.gitignore
@@ -0,0 +1,26 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
diff --git a/1.SSO单点登录/sso/pom.xml b/1.SSO单点登录/sso/pom.xml
new file mode 100644
index 0000000..7b885cb
--- /dev/null
+++ b/1.SSO单点登录/sso/pom.xml
@@ -0,0 +1,83 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+ com.example
+ sso
+ 0.0.1-SNAPSHOT
+ jar
+ sso
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 1.3.2
+
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+ 1.1.9
+
+
+
+ mysql
+ mysql-connector-java
+ 6.0.6
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.4
+
+
+ com.auth0
+ java-jwt
+ 3.7.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ CAS
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ maven-compiler-plugin
+
+
+ 1.8
+ UTF-8
+
+
+
+
+
+
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/SsoApplication.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/SsoApplication.java
new file mode 100644
index 0000000..fb9e789
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/SsoApplication.java
@@ -0,0 +1,16 @@
+package com.infinova.sso;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.RestController;
+
+@SpringBootApplication
+@RestController
+@MapperScan("com.infinova.sso.mapper")
+public class SsoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SsoApplication.class, args);
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/controller/JwtController.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/controller/JwtController.java
new file mode 100644
index 0000000..ca07929
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/controller/JwtController.java
@@ -0,0 +1,55 @@
+package com.infinova.sso.controller;
+
+import com.alibaba.fastjson.JSONArray;
+import com.infinova.sso.entity.ReturnEntity;
+import com.infinova.sso.entity.User;
+import com.infinova.sso.service.JwtService;
+import com.infinova.sso.util.HttpUtil;
+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;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/4 18:16
+ */
+@RestController
+public class JwtController {
+
+ private JwtService service;
+
+ @Autowired
+ public JwtController(JwtService service) {
+ this.service = service;
+ }
+
+ @PostMapping("/login")
+ public ReturnEntity login(@RequestBody User user) {
+ String token = service.login(user);
+ return ReturnEntity.successResult(token);
+ }
+
+ @PostMapping("/checkJwt")
+ public ReturnEntity checkJwt(@RequestBody JSONArray tokenList) {
+ return ReturnEntity.successResult(service.checkJwt(tokenList));
+ }
+
+ @GetMapping("/refreshJwt")
+ public ReturnEntity refreshJwt() {
+ String oldToken = HttpUtil.getData(JwtService.JWT_KEY);
+ String newToken = service.refreshJwt(oldToken);
+ return ReturnEntity.successResult(newToken);
+ }
+
+ @GetMapping("/inValid")
+ public ReturnEntity inValid() {
+ String token = HttpUtil.getData(JwtService.JWT_KEY);
+ service.inValid(token);
+ return ReturnEntity.successResult(null);
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/ReturnEntity.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/ReturnEntity.java
new file mode 100644
index 0000000..2892640
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/ReturnEntity.java
@@ -0,0 +1,55 @@
+package com.infinova.sso.entity;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/2/28 18:39
+ */
+public class ReturnEntity {
+ private int code;
+ private String message;
+ private Object data;
+
+ public ReturnEntity() {
+ }
+
+ public ReturnEntity(int code, String message, Object data) {
+ this.code = code;
+ this.message = message;
+ this.data = data;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public static ReturnEntity successResult(Object data) {
+ return new ReturnEntity(1, "", data);
+ }
+
+ public static ReturnEntity failedResult(String message) {
+ return new ReturnEntity(0, message, null);
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/User.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/User.java
new file mode 100644
index 0000000..152c7ef
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/User.java
@@ -0,0 +1,85 @@
+package com.infinova.sso.entity;
+
+import java.util.Date;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/2/28 18:34
+ */
+public class User {
+ private String recId;
+ private String userType;
+ private String loginId;
+ private String password;
+ private String question;
+ private String answer;
+ private Date passwordChangedDateTime;
+ private Date lastModDateTime;
+
+ public String getRecId() {
+ return recId;
+ }
+
+ public void setRecId(String recId) {
+ this.recId = recId;
+ }
+
+ public String getUserType() {
+ return userType;
+ }
+
+ public void setUserType(String userType) {
+ this.userType = userType;
+ }
+
+ public String getLoginId() {
+ return loginId;
+ }
+
+ public void setLoginId(String loginId) {
+ this.loginId = loginId;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getQuestion() {
+ return question;
+ }
+
+ public void setQuestion(String question) {
+ this.question = question;
+ }
+
+ public String getAnswer() {
+ return answer;
+ }
+
+ public void setAnswer(String answer) {
+ this.answer = answer;
+ }
+
+ public Date getPasswordChangedDateTime() {
+ return passwordChangedDateTime;
+ }
+
+ public void setPasswordChangedDateTime(Date passwordChangedDateTime) {
+ this.passwordChangedDateTime = passwordChangedDateTime;
+ }
+
+ public Date getLastModDateTime() {
+ return lastModDateTime;
+ }
+
+ public void setLastModDateTime(Date lastModDateTime) {
+ this.lastModDateTime = lastModDateTime;
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/CustomException.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/CustomException.java
new file mode 100644
index 0000000..fb10904
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/CustomException.java
@@ -0,0 +1,40 @@
+package com.infinova.sso.exception;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/4 19:28
+ */
+public class CustomException extends RuntimeException {
+
+ private String message;
+
+ public CustomException() {
+ super();
+ }
+
+
+ public CustomException(Exception e, String message) {
+ super(e);
+ this.message = message;
+ }
+
+ public CustomException(String message) {
+ super(message);
+ }
+
+ @Override
+ public String getMessage() {
+ if (message == null || message.length() == 0) {
+ return super.getMessage();
+ } else {
+ return message;
+ }
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/ExceptionHandle.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/ExceptionHandle.java
new file mode 100644
index 0000000..7ba2d4a
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/ExceptionHandle.java
@@ -0,0 +1,26 @@
+package com.infinova.sso.exception;
+
+import com.infinova.sso.entity.ReturnEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/5 11:10
+ */
+@RestControllerAdvice
+public class ExceptionHandle {
+
+ @ExceptionHandler(Exception.class)
+ public ReturnEntity handleException(Exception e) {
+ e.printStackTrace();
+ if (e instanceof CustomException) {
+ return ReturnEntity.failedResult(e.getMessage());
+ } else {
+ return ReturnEntity.failedResult("未知错误:" + e.getMessage());
+ }
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/mapper/UserMapper.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/mapper/UserMapper.java
new file mode 100644
index 0000000..1a8819f
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/mapper/UserMapper.java
@@ -0,0 +1,25 @@
+package com.infinova.sso.mapper;
+
+import com.infinova.sso.entity.User;
+import org.apache.ibatis.annotations.Select;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/5 9:34
+ */
+public interface UserMapper {
+
+ /**
+ * Description:通过loginID获取用户信息
+ *
+ * @author fanxb
+ * @date 2019/3/5 9:55
+ * @param id
+ * @return com.infinova.sso.entity.User
+ */
+ @Select("select RecID recId,LoginID loginId,Pwd password from siteviewusers where LoginID=#{id}")
+ User selectByLoginId(String id);
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/ArrayUtil.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/ArrayUtil.java
new file mode 100644
index 0000000..33be405
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/ArrayUtil.java
@@ -0,0 +1,26 @@
+package com.infinova.sso.util;
+
+/**
+ * 类功能简述: 数组工具类
+ *
+ * @author fanxb
+ * @date 2019/3/4 17:06
+ */
+public class ArrayUtil {
+ /**
+ * Description: 从指定数组指定下标复制指定长度到目标数组指定下标
+ *
+ * @author fanxb
+ * @date 2019/3/4 17:16
+ * @param source 源数组
+ * @param sourceStart 原数组copy开始下标
+ * @param target 目标数组
+ * @param targetStart 目标数组接收开始下标
+ * @param length copy长度
+ */
+ public static void copyTo(byte[] source, int sourceStart, byte[] target, int targetStart, int length) {
+ for (int i = 0; i < length; i++) {
+ target[targetStart + i] = source[sourceStart + i];
+ }
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/HttpUtil.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/HttpUtil.java
new file mode 100644
index 0000000..3529fb6
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/HttpUtil.java
@@ -0,0 +1,52 @@
+package com.infinova.sso.util;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/5 10:12
+ */
+public class HttpUtil {
+ /**
+ * Description: 从httprequest中的url参数/header/cookie中取某个值
+ *
+ * @param key 关键词
+ * @return java.lang.String
+ * @author fanxb
+ * @date 2019/3/5 10:13
+ */
+ public static String getData(String key) {
+ HttpServletRequest request = getRequest();
+ String value = request.getHeader(key);
+ if (value == null) {
+ value = request.getParameter(key);
+ }
+ if (value == null) {
+ Cookie[] cookies = request.getCookies();
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().toLowerCase().equals(key.toLowerCase())) {
+ value = cookie.getValue();
+ }
+ }
+ }
+ return value;
+ }
+
+ public static HttpServletRequest getRequest() {
+ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ return requestAttributes.getRequest();
+ }
+
+ public static HttpServletResponse getResponse() {
+ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ return requestAttributes.getResponse();
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/JwtUtil.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/JwtUtil.java
new file mode 100644
index 0000000..5f03719
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/JwtUtil.java
@@ -0,0 +1,62 @@
+package com.infinova.sso.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.infinova.sso.exception.CustomException;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/2/28 18:36
+ */
+public class JwtUtil {
+
+ /**
+ * Description: 生成一个jwt字符串
+ *
+ * @param recId 用户id
+ * @param loginId 登录id
+ * @param secret 秘钥
+ * @param timeOut 超时时间(单位s)
+ * @return java.lang.String
+ * @author fanxb
+ * @date 2019/3/4 17:26
+ */
+ public static String encode(String loginId, String secret, long timeOut) {
+ Algorithm algorithm = Algorithm.HMAC256(secret);
+ String token = JWT.create()
+ //设置过期时间为一个小时
+ .withExpiresAt(new Date(System.currentTimeMillis() + timeOut * 1000))
+ //设置负载
+ .withClaim("loginId", loginId)
+ .sign(algorithm);
+ return token;
+ }
+
+ /**
+ * Description: 解密jwt
+ *
+ * @param token token
+ * @param secret secret
+ * @return java.util.Map
+ * @author fanxb
+ * @date 2019/3/4 18:14
+ */
+ public static Map decode(String token, String secret) {
+ if (token == null || token.length() == 0) {
+ throw new CustomException("token为空:" + token);
+ }
+ Algorithm algorithm = Algorithm.HMAC256(secret);
+ JWTVerifier jwtVerifier = JWT.require(algorithm).build();
+ DecodedJWT decodedJWT = jwtVerifier.verify(token);
+ return decodedJWT.getClaims();
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/PasswordUtil.java b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/PasswordUtil.java
new file mode 100644
index 0000000..0fb1f6b
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/PasswordUtil.java
@@ -0,0 +1,80 @@
+package com.infinova.sso.util;
+
+import javax.xml.bind.DatatypeConverter;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/4 17:20
+ */
+public class PasswordUtil {
+ /**
+ * Description: plainText加salt使用sha1计算hash
+ *
+ * @param plainText 原文
+ * @param salt 盐
+ * @return byte[]
+ * @author fanxb
+ * @date 2019/3/4 17:20
+ */
+ private static byte[] computeHashValue(String plainText, byte[] salt) throws Exception {
+ byte[] plainBytes = plainText.getBytes("utf-16LE");
+ byte[] arr = new byte[salt.length + plainBytes.length];
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+ ArrayUtil.copyTo(plainBytes, 0, arr, 0, plainBytes.length);
+ ArrayUtil.copyTo(salt, 0, arr, plainBytes.length, salt.length);
+ return md.digest(arr);
+ }
+
+
+ /**
+ * Description: 根据原文计算出密文,计算方法:使用一个随机数做盐值和密码一起使用sha1做hash,再和盐值拼接到一起,
+ * 最后生成base64字符串
+ *
+ * @param pass 原文
+ * @return java.lang.String
+ * @author fanxb
+ * @date 2019/3/4 17:21
+ */
+ public static String createPassword(String pass) throws Exception {
+ byte[] salt = SecureRandom.getSeed(0x10);
+ byte[] buffer2 = computeHashValue(pass, salt);
+ byte[] array = new byte[0x24];
+ ArrayUtil.copyTo(salt, 0, array, 0, salt.length);
+ ArrayUtil.copyTo(buffer2, 0, array, salt.length, salt.length);
+ return DatatypeConverter.printBase64Binary(array);
+ }
+
+ /**
+ * Description: 判断密文原文是否一致
+ *
+ * @param cipherPassword 密文
+ * @param plainPassword 原文
+ * @return boolean
+ * @author fanxb
+ * @date 2019/3/4 17:23
+ */
+ public static boolean checkPassword(String cipherPassword, String plainPassword) {
+ try {
+ byte[] cipherPasswordBytes = DatatypeConverter.parseBase64Binary(cipherPassword);
+ byte[] saltBytes = new byte[0x10];
+ ArrayUtil.copyTo(cipherPasswordBytes, 0, saltBytes, 0, saltBytes.length);
+ byte[] passwordBytes = computeHashValue(plainPassword, saltBytes);
+ byte[] realPasswordBytes = new byte[20];
+ ArrayUtil.copyTo(cipherPasswordBytes, saltBytes.length, realPasswordBytes, 0, realPasswordBytes.length);
+ for (int i = 0; i < realPasswordBytes.length; i++) {
+ if (passwordBytes[i] != realPasswordBytes[i]) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/1.SSO单点登录/sso/src/main/resources/application.yml b/1.SSO单点登录/sso/src/main/resources/application.yml
new file mode 100644
index 0000000..168bf40
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/resources/application.yml
@@ -0,0 +1,14 @@
+server:
+ port: 8081
+ servlet:
+ context-path: /sso
+spring:
+ application:
+ name: SSO
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ druid:
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://localhost:3308/itoss?useUnicode=true&characterEncoding=gbk&serverTimezone=UTC&useSSL=false
+ username: root
+ password: root
diff --git a/1.SSO单点登录/sso/src/main/resources/static/index.html b/1.SSO单点登录/sso/src/main/resources/static/index.html
new file mode 100644
index 0000000..be5c304
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/resources/static/index.html
@@ -0,0 +1,64 @@
+
+
+
+
+ 认证中心
+
+
+这里是认证中心主页
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.SSO单点登录/sso/src/main/resources/static/login.html b/1.SSO单点登录/sso/src/main/resources/static/login.html
new file mode 100644
index 0000000..e0e5da1
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/resources/static/login.html
@@ -0,0 +1,42 @@
+
+
+
+
+ 认证中心登录页面
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.SSO单点登录/sso/src/main/resources/static/main.js b/1.SSO单点登录/sso/src/main/resources/static/main.js
new file mode 100644
index 0000000..07c0a87
--- /dev/null
+++ b/1.SSO单点登录/sso/src/main/resources/static/main.js
@@ -0,0 +1,24 @@
+function getToken() {
+ return window.localStorage.token || null;
+}
+
+function setToken(token) {
+ window.localStorage.token = token;
+}
+
+function getUserName() {
+ var token = getToken();
+ if (token == null) {
+ return "未登录";
+ } else {
+ var info = token.split(".")[1];
+ return JSON.parse(window.atob(info)).name;
+ }
+}
+
+function getUrlParam(name) {
+ var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
+ var r = window.location.search.substr(1).match(reg); //匹配目标参数
+ if (r != null) return unescape(r[2]);
+ return null; //返回参数值
+}
\ No newline at end of file
diff --git a/1.SSO单点登录/sso/src/test/java/com/infinova/sso/service_test/JwtServiceTest.java b/1.SSO单点登录/sso/src/test/java/com/infinova/sso/service_test/JwtServiceTest.java
new file mode 100644
index 0000000..9c245f4
--- /dev/null
+++ b/1.SSO单点登录/sso/src/test/java/com/infinova/sso/service_test/JwtServiceTest.java
@@ -0,0 +1,38 @@
+package com.infinova.sso.service_test;
+
+import com.infinova.sso.entity.User;
+import com.infinova.sso.service.JwtService;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/5 8:57
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class JwtServiceTest {
+
+ @Autowired
+ private JwtService jwtService;
+
+ @Test
+ public void loginTest() throws Exception {
+ User user = new User();
+ user.setLoginId("admin");
+ user.setPassword(DatatypeConverter.printBase64Binary("manage".getBytes("utf-8")));
+ String token = jwtService.login(user);
+ Assert.assertTrue(token != null && token.length() > 0);
+ }
+
+}
diff --git a/1.SSO单点登录/sys-a/.gitignore b/1.SSO单点登录/sys-a/.gitignore
new file mode 100644
index 0000000..ba5cb5c
--- /dev/null
+++ b/1.SSO单点登录/sys-a/.gitignore
@@ -0,0 +1,26 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+/build/
diff --git a/1.SSO单点登录/sys-a/pom.xml b/1.SSO单点登录/sys-a/pom.xml
new file mode 100644
index 0000000..63fe3b5
--- /dev/null
+++ b/1.SSO单点登录/sys-a/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+ com.example
+ sys-a
+ 0.0.1-SNAPSHOT
+ sys-a
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.squareup.okhttp3
+ okhttp
+ 3.13.1
+
+
+ com.alibaba
+ fastjson
+ 1.2.4
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/SysAApplication.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/SysAApplication.java
new file mode 100644
index 0000000..6e48b62
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/SysAApplication.java
@@ -0,0 +1,16 @@
+package com.example.sysa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SysAApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SysAApplication.class, args);
+ }
+
+
+
+
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/controller/Main.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/controller/Main.java
new file mode 100644
index 0000000..52323b7
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/controller/Main.java
@@ -0,0 +1,33 @@
+package com.example.sysa.controller;
+
+import com.example.sysa.entity.ReturnEntity;
+import com.example.sysa.entity.UserContext;
+import com.example.sysa.filter.LoginFilter;
+import com.example.sysa.util.HttpClient;
+import com.example.sysa.util.UserContextHolder;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/1 14:08
+ */
+@RestController
+public class Main {
+
+ @RequestMapping("/test")
+ public ReturnEntity test() {
+ return new ReturnEntity(1, "通过验证", null);
+ }
+
+ @RequestMapping("/logout")
+ public ReturnEntity logout() throws Exception{
+ UserContext context = UserContextHolder.get();
+ HttpClient.get("http://localhost:8080/clearToken?token="+context.getToken());
+ LoginFilter.tokenMap.remove(context.getToken());
+ return null;
+ }
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/ReturnEntity.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/ReturnEntity.java
new file mode 100644
index 0000000..62d1126
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/ReturnEntity.java
@@ -0,0 +1,47 @@
+package com.example.sysa.entity;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/2/28 18:39
+ */
+public class ReturnEntity {
+ private int code;
+ private String message;
+ private Object data;
+
+ public ReturnEntity() {
+ }
+
+ public ReturnEntity(int code, String message, Object data) {
+ this.code = code;
+ this.message = message;
+ this.data = data;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/UserContext.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/UserContext.java
new file mode 100644
index 0000000..ad3aa23
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/UserContext.java
@@ -0,0 +1,38 @@
+package com.example.sysa.entity;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/1 14:26
+ */
+public class UserContext {
+
+ private String id;
+ private String token;
+
+ public UserContext() {
+ }
+
+ public UserContext(String id, String token) {
+ this.id = id;
+ this.token = token;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/filter/LoginFilter.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/filter/LoginFilter.java
new file mode 100644
index 0000000..59c9128
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/filter/LoginFilter.java
@@ -0,0 +1,89 @@
+package com.example.sysa.filter;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.sysa.entity.ReturnEntity;
+import com.example.sysa.entity.UserContext;
+import com.example.sysa.util.HttpClient;
+import com.example.sysa.util.UserContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Base64Utils;
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/1 10:35
+ */
+@Component
+@WebFilter(urlPatterns = "/**", filterName = "loginFilter")
+public class LoginFilter implements Filter {
+
+ public static final Map tokenMap = new ConcurrentHashMap<>();
+ /**
+ * 10s内认为报错在tokenMap中的token有效,避免频繁请求认证中心验证token有效性
+ */
+ private static final int EXPIRE_TIME = 10 * 1000;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+ if (httpServletRequest.getRequestURI().startsWith("/static")) {
+ UserContextHolder.remove();
+ filterChain.doFilter(servletRequest, servletResponse);
+ return;
+ }
+ boolean isOk = false;
+ String token = "";
+ try {
+ //如果token未缓存或距离上次检查有效性超过1分钟,向认证中心请求检查有效性
+ token = httpServletRequest.getParameter("token");
+ Long lastCheckTime = tokenMap.get(token);
+ if (lastCheckTime == null || System.currentTimeMillis() - lastCheckTime > EXPIRE_TIME) {
+ JSONObject object = HttpClient.get("http://localhost:8080/checkToken?token=" + token);
+ if (object.getInteger("code") == 1) {
+ isOk = true;
+ tokenMap.put(token, System.currentTimeMillis());
+ }
+ } else {
+ //token有效,且未过期
+ isOk = true;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (isOk) {
+ //token和用户id保存到userContextholder
+ String str = new String(Base64Utils.decodeFromString(token.split("\\.")[1]));
+ UserContext context = new UserContext(JSON.parseObject(str).getString("name"), token);
+ UserContextHolder.set(context);
+ filterChain.doFilter(servletRequest, servletResponse);
+ } else {
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ response.setContentType("application/json;charset=utf-8");
+ response.setCharacterEncoding("utf-8");
+ response.getWriter().write(JSON.toJSONString(new ReturnEntity(-1, "未登录", null)));
+ }
+
+ }
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/HttpClient.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/HttpClient.java
new file mode 100644
index 0000000..c14f6d6
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/HttpClient.java
@@ -0,0 +1,26 @@
+package com.example.sysa.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/1 14:10
+ */
+public class HttpClient {
+ private static final OkHttpClient CLIENT = new OkHttpClient();
+
+ public static JSONObject get(String url)throws Exception{
+ Request request = new Request.Builder().url(url).build();
+ Response response = CLIENT.newCall(request).execute();
+ return JSON.parseObject(response.body().string());
+ }
+
+
+}
diff --git a/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/UserContextHolder.java b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/UserContextHolder.java
new file mode 100644
index 0000000..1f1f11c
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/UserContextHolder.java
@@ -0,0 +1,29 @@
+package com.example.sysa.util;
+
+
+import com.example.sysa.entity.UserContext;
+
+/**
+ * 类功能简述:
+ * 类功能详述:
+ *
+ * @author fanxb
+ * @date 2019/3/1 14:26
+ */
+public class UserContextHolder {
+
+ private static final ThreadLocal USER_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
+
+ public static UserContext get() {
+ return USER_CONTEXT_THREAD_LOCAL.get();
+ }
+
+ public static void set(UserContext context) {
+ USER_CONTEXT_THREAD_LOCAL.set(context);
+ }
+
+ public static void remove() {
+ USER_CONTEXT_THREAD_LOCAL.remove();
+ }
+
+}
diff --git a/1.SSO单点登录/sys-a/src/main/resources/application.properties b/1.SSO单点登录/sys-a/src/main/resources/application.properties
new file mode 100644
index 0000000..6f97ef9
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+server.port=8081
+spring.mvc.static-path-pattern=/static/**
\ No newline at end of file
diff --git a/1.SSO单点登录/sys-a/src/main/resources/static/index.html b/1.SSO单点登录/sys-a/src/main/resources/static/index.html
new file mode 100644
index 0000000..df06055
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/resources/static/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+ 系统A
+
+
+这里是系统A主页面
+当前登录用户:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.SSO单点登录/sys-a/src/main/resources/static/main.js b/1.SSO单点登录/sys-a/src/main/resources/static/main.js
new file mode 100644
index 0000000..07c0a87
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/resources/static/main.js
@@ -0,0 +1,24 @@
+function getToken() {
+ return window.localStorage.token || null;
+}
+
+function setToken(token) {
+ window.localStorage.token = token;
+}
+
+function getUserName() {
+ var token = getToken();
+ if (token == null) {
+ return "未登录";
+ } else {
+ var info = token.split(".")[1];
+ return JSON.parse(window.atob(info)).name;
+ }
+}
+
+function getUrlParam(name) {
+ var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
+ var r = window.location.search.substr(1).match(reg); //匹配目标参数
+ if (r != null) return unescape(r[2]);
+ return null; //返回参数值
+}
\ No newline at end of file
diff --git a/1.SSO单点登录/sys-a/src/main/resources/static/redirect.html b/1.SSO单点登录/sys-a/src/main/resources/static/redirect.html
new file mode 100644
index 0000000..daf0a2c
--- /dev/null
+++ b/1.SSO单点登录/sys-a/src/main/resources/static/redirect.html
@@ -0,0 +1,18 @@
+
+
+
+
+ 登录成功回调地址
+
+
+
+
+
+
+
+
\ No newline at end of file