From 47686a30cb9385eb11b81bccd87062bf5d75ce20 Mon Sep 17 00:00:00 2001 From: fanxb Date: Thu, 7 Mar 2019 19:56:30 +0800 Subject: [PATCH] =?UTF-8?q?sso=E5=8D=95=E7=82=B9=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- 1.SSO单点登录/sso/.gitignore | 26 ++++++ 1.SSO单点登录/sso/pom.xml | 83 +++++++++++++++++ .../java/com/infinova/sso/SsoApplication.java | 16 ++++ .../infinova/sso/controller/JwtController.java | 55 ++++++++++++ .../com/infinova/sso/entity/ReturnEntity.java | 55 ++++++++++++ .../main/java/com/infinova/sso/entity/User.java | 85 ++++++++++++++++++ .../infinova/sso/exception/CustomException.java | 40 +++++++++ .../infinova/sso/exception/ExceptionHandle.java | 26 ++++++ .../java/com/infinova/sso/mapper/UserMapper.java | 25 ++++++ .../java/com/infinova/sso/util/ArrayUtil.java | 26 ++++++ .../main/java/com/infinova/sso/util/HttpUtil.java | 52 +++++++++++ .../main/java/com/infinova/sso/util/JwtUtil.java | 62 +++++++++++++ .../java/com/infinova/sso/util/PasswordUtil.java | 80 +++++++++++++++++ .../sso/src/main/resources/application.yml | 14 +++ .../sso/src/main/resources/static/index.html | 64 +++++++++++++ .../sso/src/main/resources/static/login.html | 42 +++++++++ .../sso/src/main/resources/static/main.js | 24 +++++ .../infinova/sso/service_test/JwtServiceTest.java | 38 ++++++++ 1.SSO单点登录/sys-a/.gitignore | 26 ++++++ 1.SSO单点登录/sys-a/pom.xml | 53 +++++++++++ .../java/com/example/sysa/SysAApplication.java | 16 ++++ .../java/com/example/sysa/controller/Main.java | 33 +++++++ .../com/example/sysa/entity/ReturnEntity.java | 47 ++++++++++ .../java/com/example/sysa/entity/UserContext.java | 38 ++++++++ .../java/com/example/sysa/filter/LoginFilter.java | 89 +++++++++++++++++++ .../java/com/example/sysa/util/HttpClient.java | 26 ++++++ .../com/example/sysa/util/UserContextHolder.java | 29 ++++++ .../src/main/resources/application.properties | 2 + .../sys-a/src/main/resources/static/index.html | 49 ++++++++++ .../sys-a/src/main/resources/static/main.js | 24 +++++ .../sys-a/src/main/resources/static/redirect.html | 18 ++++ 32 files changed, 1267 insertions(+), 1 deletion(-) create mode 100644 1.SSO单点登录/sso/.gitignore create mode 100644 1.SSO单点登录/sso/pom.xml create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/SsoApplication.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/controller/JwtController.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/ReturnEntity.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/entity/User.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/CustomException.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/exception/ExceptionHandle.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/mapper/UserMapper.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/ArrayUtil.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/HttpUtil.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/JwtUtil.java create mode 100644 1.SSO单点登录/sso/src/main/java/com/infinova/sso/util/PasswordUtil.java create mode 100644 1.SSO单点登录/sso/src/main/resources/application.yml create mode 100644 1.SSO单点登录/sso/src/main/resources/static/index.html create mode 100644 1.SSO单点登录/sso/src/main/resources/static/login.html create mode 100644 1.SSO单点登录/sso/src/main/resources/static/main.js create mode 100644 1.SSO单点登录/sso/src/test/java/com/infinova/sso/service_test/JwtServiceTest.java create mode 100644 1.SSO单点登录/sys-a/.gitignore create mode 100644 1.SSO单点登录/sys-a/pom.xml create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/SysAApplication.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/controller/Main.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/ReturnEntity.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/entity/UserContext.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/filter/LoginFilter.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/HttpClient.java create mode 100644 1.SSO单点登录/sys-a/src/main/java/com/example/sysa/util/UserContextHolder.java create mode 100644 1.SSO单点登录/sys-a/src/main/resources/application.properties create mode 100644 1.SSO单点登录/sys-a/src/main/resources/static/index.html create mode 100644 1.SSO单点登录/sys-a/src/main/resources/static/main.js create mode 100644 1.SSO单点登录/sys-a/src/main/resources/static/redirect.html 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 + 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