sso单点登录增加

This commit is contained in:
fanxb 2019-03-07 19:56:30 +08:00
parent 284c57dbed
commit 47686a30cb
32 changed files with 1267 additions and 1 deletions

3
.gitignore vendored
View File

@ -47,3 +47,6 @@ hs_err_pid*
/nbdist/ /nbdist/
/.nb-gradle/ /.nb-gradle/
target target
.mvn
mvnw
mvnw.cmd

26
1.SSO单点登录/sso/.gitignore vendored Normal file
View File

@ -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/

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sso</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sso</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--alibaba连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>CAS</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}

View File

@ -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];
}
}
}

View File

@ -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();
}
}

View File

@ -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<java.lang.String , com.auth0.jwt.interfaces.Claim>
* @author fanxb
* @date 2019/3/4 18:14
*/
public static Map<String, Claim> 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();
}
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>认证中心</title>
</head>
<body onload="steal()">
<div style="text-align: center;">这里是认证中心主页</div>
<div>
<button onclick="clearToken()">清除token</button>
</div>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="main.js"></script>
<script src="https://cdn.bootcss.com/Base64/1.0.2/base64.js"></script>
<script>
$('#loginStatus').html(getUserName())
function clearToken() {
delete window.localStorage.token;
}
var order = getUrlParam("order");
switch (order) {
case "checkLogin":
checkLogin();
break;
case "updateToken":
updateToken();
break;
default:
console.log("不支持的order:" + order);
}
function checkLogin() {
var token = getToken();
if (token == null) {
goLogin();
} else {
//有token检查token是否还有效
$.get("/checkToken?token=" + getToken(), function (res) {
if (res.code === 1) {
alert('以登录,跳转到回调页面');
window.location.href = getUrlParam("redirect") + "&token=" + getToken();
}else {
goLogin();
}
})
}
}
function goLogin(){
// alert("无认证信息,即将跳转到登录页面");
window.location.href = encodeURI("/login.html?redirect=" + getUrlParam("redirect"));
}
function updateToken() {
var token = getUrlParam("token");
setToken(token);
}
</script>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>认证中心登录页面</title>
</head>
<body>
<div style="width:800px;margin:0 auto">
<input type="text" value="" id="name"/><br/>
<input type="password" value="" id="password"/><br/>
<button id="login" onclick="doLogin()">登录</button>
</div>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="main.js"></script>
<script src="https://cdn.bootcss.com/Base64/1.0.2/base64.min.js"></script>
<script>
function doLogin() {
var name = $('#name').val();
var password = $('#password').val();
password = window.btoa(password);
console.log(name, password);
$.ajax({
type: 'post',
url: "/sso/login",
contentType: "application/json",
dataType: 'json',
data: JSON.stringify({loginId: name, password: password}),
success: function (res) {
if (res.code === 1) {
setToken(res.data);
// alert("登录成功,跳转到回调地址");
// window.location.href = getUrlParam("redirect") + "&token=" + res.data;
} else {
alert("账号密码错误");
}
}
})
}
</script>
</body>
</html>

View File

@ -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; //返回参数值
}

View File

@ -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);
}
}

26
1.SSO单点登录/sys-a/.gitignore vendored Normal file
View File

@ -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/

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sys-a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sys-a</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.13.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String, Long> 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)));
}
}
}

View File

@ -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());
}
}

View File

@ -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<UserContext> 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();
}
}

View File

@ -0,0 +1,2 @@
server.port=8081
spring.mvc.static-path-pattern=/static/**

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>系统A</title>
</head>
<body>
<div style="align-content: center">这里是系统A主页面</div>
<div style="align-content: center">当前登录用户:<span id="loginStatus"></span></div>
<div>
<button onclick="test()">测试接口访问</button>
<button onclick="clearToken()">注销登录</button>
</div>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="/static/main.js"></script>
<script src="https://cdn.bootcss.com/Base64/1.0.2/base64.min.js"></script>
<script>
$('#loginStatus').text(getUserName());
if (getToken() == null) {
goToLoginServer();
}
test();
function test() {
$.get("/test?token=" + getToken(), function (res) {
if (res.code === 1) {
} else {
goToLoginServer();
}
})
}
//注销登录
function clearToken() {
$.get("/logout?token=" + getToken(), function (res) {
goToLoginServer();
})
}
function goToLoginServer() {
alert("当前无登录信息,跳转到认证中心");
location.href = encodeURI("http://localhost:8080?order=checkLogin&redirect=" + window.location.origin
+ "/static/redirect.html?redirect=" + window.location.href);
}
</script>
</body>
</html>

View File

@ -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; //返回参数值
}

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录成功回调地址</title>
</head>
<body>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="/static/main.js"></script>
<script src="https://cdn.bootcss.com/Base64/1.0.2/base64.min.js"></script>
<script>
var token = getUrlParam("token");
setToken(token);
// alert("认证成功,准备跳回登录登录前访问地址");
window.location.href = getUrlParam("redirect");
</script>
</body>
</html>