sso单点登录增加
This commit is contained in:
parent
284c57dbed
commit
47686a30cb
5
.gitignore
vendored
5
.gitignore
vendored
@ -46,4 +46,7 @@ hs_err_pid*
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
target
|
||||
target
|
||||
.mvn
|
||||
mvnw
|
||||
mvnw.cmd
|
26
1.SSO单点登录/sso/.gitignore
vendored
Normal file
26
1.SSO单点登录/sso/.gitignore
vendored
Normal 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/
|
83
1.SSO单点登录/sso/pom.xml
Normal file
83
1.SSO单点登录/sso/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
14
1.SSO单点登录/sso/src/main/resources/application.yml
Normal file
14
1.SSO单点登录/sso/src/main/resources/application.yml
Normal 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
|
64
1.SSO单点登录/sso/src/main/resources/static/index.html
Normal file
64
1.SSO单点登录/sso/src/main/resources/static/index.html
Normal 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>
|
42
1.SSO单点登录/sso/src/main/resources/static/login.html
Normal file
42
1.SSO单点登录/sso/src/main/resources/static/login.html
Normal 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>
|
24
1.SSO单点登录/sso/src/main/resources/static/main.js
Normal file
24
1.SSO单点登录/sso/src/main/resources/static/main.js
Normal 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; //返回参数值
|
||||
}
|
@ -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
26
1.SSO单点登录/sys-a/.gitignore
vendored
Normal 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/
|
53
1.SSO单点登录/sys-a/pom.xml
Normal file
53
1.SSO单点登录/sys-a/pom.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
server.port=8081
|
||||
spring.mvc.static-path-pattern=/static/**
|
49
1.SSO单点登录/sys-a/src/main/resources/static/index.html
Normal file
49
1.SSO单点登录/sys-a/src/main/resources/static/index.html
Normal 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>
|
24
1.SSO单点登录/sys-a/src/main/resources/static/main.js
Normal file
24
1.SSO单点登录/sys-a/src/main/resources/static/main.js
Normal 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; //返回参数值
|
||||
}
|
18
1.SSO单点登录/sys-a/src/main/resources/static/redirect.html
Normal file
18
1.SSO单点登录/sys-a/src/main/resources/static/redirect.html
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user