10_单点登录SSO

是什么

在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,运营人员每天用自己 的账号登录,很方便。但随着企业的发展,用到的系统随之增多,运营人员在操作不同的系统时,需要多次登录, 而且每个系统的账号都不一样,这对于运营人员来说,很不方便。于是,就想到是不是可以在一个系统登录,其他系统就不用登录了呢?这就是单点登录要解决的问题。

单点登录英文全称Single Sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统


Cookie-Session登录

10_单点登录SSO

我们在浏览器(Browser)中访问一个应用,这个应用需要登录,我们填写完用户名和密码后,完成 登录认证。这时,我们在这个用户的session中标记登录状态为yes(已登录),同时在浏览器(Browser)中写入Cookie,这个Cookie是这个用户的唯一标识。下次我们再访问这个应用的时候,请求中会带上这个Cookie,服务端会根据这个Cookie找到对应的session,通过session来判断这个用户是否登录。如果不做特殊配置,这个 Cookie的名字叫做jsessionid,值在服务端(server)是唯一的


Cookie-Session解决单点登录

场景: 有个域名叫做:a.com,同时有两个业务系统分别为:app1.a.com和 app2.a.com。我们要做单点登录(SSO),需要一个登录系统,叫做: sso.a.com,需要实现只要在sso.a.com登录,app1.a.com和app2.a.com就也登录了

解决问题1: Cookie不能跨域问题

sso登录以后,可以将Cookie的域设置为顶域,即.a.com,这样所有子域的系统都可以访问到顶域的Cookie。我们在设置Cookie时,只能设置顶域和自己的域,不能设置其他的域。比如:我们不能在自己的系统中给baidu.com的域设置Cookie

解决问题2: Session不能共享问题

我们在sso系统登录了,这时再访问app1,Cookie也带到了 app1的服务端(Server),app1的服务端怎么找到这个Cookie对应的Session呢?这里就要把3个系统的Session共享,如图所示。共享Session的解决方案有很多,例如:SpringSession。

10_单点登录SSO

基于token的认证登录

当我们谈到利用token进行认证,我们一般说的就是利用JSON Web Tokens(JWTs)进行认证

基于token的身份验证是无状态的,服务器不需要记录哪些用户已经登录或者哪些JWTs已经处理。每个发送到服务器的请求都会带上一个token,服务器利用这个token检查确认请求的真实性

如果说Cookie-Session登录方式可以看成是去银行办理的银行卡,你的银行卡信息都记录在银行

那么可以把token理解成一张演唱会的门票。服务器(演唱会主办方)每次只需要检查你这张门票的有效性,不需要知 道你这张门票是在哪里买的,从谁买的,什么时候买的等等。不同等级的门票可以坐的位置不同,同样的,权限不同的用户可以进行的操作也不同。

10_单点登录SSO


是什么

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任

通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据


应用场景

授权。

这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛


优缺点

优点:

  1. 因为token存储在客户端,服务器只负责解码。这样不需要占用服务器端资源。

  2. 服务器端可以无限扩展,负载均衡器可以将用户传递到任何服务器,服务器都能知道用户信息,因为jwt里面包含了。

  3. 数据安全,因为有签名,防止了篡改,但信息还是透明的,不要放敏感信息。

  4. 放入请求头提交,很好的防止了csrf攻击

缺点:

  1. 安全性。由于jwt的payload是使用base64编码的,并没有加密,因此jwt中不能存储敏感数据。而session1的信息是存在服务端的,相对来说更安全
  2. 性能 jwt太长。由于是无状态使用JWT,所有的数据都被放到JWT里,如果还要进行一些数据交换,那载荷会更大,经过编码之后导致jwt非常长,cookie的限制大小一般是4k,cookie很可能放不下,所以jwt一般放在 local storage 里面。并且用户在系统中的每一次http请求都会把jwt携带在Header里面,http请求的Header 可能比Body还要大。而sessionId只是很短的一个字符串,因此使用jwt的http请求比使用session的开销大得多。
  3. 一次性无状态。无法废弃。通过上面jwt的验证机制可以看出来,一旦签发一个jwt,在到期之前就会始终有效,无法中途废弃。例如你在payload中存储了一些信息,当信息需要更新时,则重新签发一个jwt,但是由于旧的jwt还没过期,拿着这个旧的jwt依旧可以登录,那登录后服务端从jwt中拿到的信息就是过时的。

使用方法

结构

正常的JWT数据结构应该如下

10_单点登录SSO

它是一个很长的字符串,中间用点(.)分隔成三个部分

JWT的三个部分依次: Header - 头部 、Payload - 负载 、Signature(签名)

即:Header.Payload.Signature

对token进行Base64解码:


Header
{
	alg: "HS256", # alg属性表示签名的算法,默认算法为HS256
	typ: "JWT" # typ属性表示这个令牌的类型,JWT令牌 就为JWT
 }

这个是JWT第一段数据,表示头部信息,主要的作用是描述JWT的元数据

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

iss (issuer):签发人

exp (expiration time):过期时间

sub (subject):主题

aud (audience):受众

nbf (Not Before):生效时间

iat (Issued At):签发时间

jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段

{
“sub”: “1234567890”,
“name”: “John Doe”,
“age”: “19”
}

注意:JWT默认是明文展示,任何人都可以读取到,所以此处不要放私密信息

这个 JSON 对象也要使用 Base64URL 算法转成字符串。


Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

  HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。


单点登录微服务

单点登录微服务父项目:supergo_sso

1、建Module:supergo_sso

2、改pom

<?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">
    <parent>
        <artifactId>supergo_parent1</artifactId>
        <groupId>com.supergo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>supergo_sso</artifactId>

    <!--父项目-->
    <packaging>pom</packaging>
</project>

单点登录微服务提供者:supergo_sso_service

1、建Module:supergo_sso_service

2、改pom

<?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">
    <parent>
        <artifactId>supergo_sso</artifactId>
        <groupId>com.supergo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>supergo_sso_service</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.supergo</groupId>
            <artifactId>supergo-base-service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--jwt 依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!--加密依赖-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
    </dependencies>

</project>

3、启动类

package com.supergo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.supergo.mapper")
public class SsoApplication9005 {
    public static void main(String[] args) {
        SpringApplication.run(SsoApplication9005.class, args);
    }
}

4、建yml

# 端口
server:
  port: 9005

# 名字
spring:
  application:
    name: supergo-manager # 代表的就是我以什么样的名字入驻进的注册中心
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver # mysql驱动类
    url: jdbc:mysql://127.0.0.1:3306/supergo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: 123456

mybatis:
  type-aliases-package: com.supergo.pojo #所有别名类所在的包
eureka:
  client:
    register-with-eureka: true # 表示将自己注册到 eureka server ,默认为 true
    fetch-registry: true # 表示是否从eureka server 抓取已有的注册信息,默认为true。单节点为所谓,集群必须为 true,才能配合ribbon使用负载均衡
    service-url:
      # 单机版:只用注册进一个服务中心【defaultZone: http://127.0.0.1:7001/eureka/】
      defaultZone: http://eureka7001.com:7001/eureka/
      # 集群版:需要同时注册进每个注册中心
  #      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com/eureka/
  # 显示的服务主机名称
  instance:
    prefer-ip-address: true # 访问路径显示 ip【统一:方便调试】
    ip-address: 127.0.0.1
    instance-id: ${eureka.instance.ip-address}.${server.port}
    lease-renewal-interval-in-seconds: 3
    lease-expiration-duration-in-seconds: 10

#actuator服务监控与管理
management:
  endpoint:
    #开启端点
    shutdown:
      enabled: true
    health:
      show-details: always
  # 加载所有的端点
  endpoints:
    web:
      exposure:
        include: "*"

# 配置生成token所需参数[自定义]
jwt:
  config:
    key: abc123 # 生成token的密钥
    ttl: 30  # 过期时间

5、JWT测试

token创建
@Test
public void createJwt() {
    //生成token
    String token = Jwts.builder()
        .setId("123")
        .setSubject("admin") //主题
        .setIssuedAt(new Date()) //签发时间
        .signWith(SignatureAlgorithm.HS256, "abc12")//签名
        .compact();
    System.out.println(token);
}

10_单点登录SSO


token解析

jwt在线工具对token解析: https://jwt.io/

将生成的token进行解析:

10_单点登录SSO


代码实现:

@Test
public void parseJwt() {

    String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTYxMDU5MjkxNX0.jzn50cbf9DhuodVey6pty0WKl3Uvl0x_d7ChRvXOkaA";
    
    //解析token
    Jws<Claims> jws = Jwts.parser().setSigningKey("abc123").parseClaimsJws(token);
    Claims claims = jws.getBody(); //获取payload部分
    System.out.println(claims);
}

10_单点登录SSO


token过期校验

token过期后,便不可以再使用

@Test
public void expireJwt() {
    //过期时间:当前时间+10s
    long exp = System.currentTimeMillis() + 10 * 1000;

    //生成token
    String token = Jwts.builder()
        .setId("1234")
        .setSubject("admin")
        .setIssuedAt(new Date()) //当前时间
        .setExpiration(new Date(exp)) // 过期时间
        .signWith(SignatureAlgorithm.HS256, "a123")//签名
        .compact();
    //读取token
    Jws<Claims> jws = Jwts.parser().setSigningKey("a123").parseClaimsJws(token);
    Claims claims = jws.getBody();
    System.out.println(claims);

    //等待10s
    for (int i = 10; i > 0; i--) {
        System.out.println("倒计时 " + i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //再次读取token[参数1:签名,参数2:token]
    jws = Jwts.parser().setSigningKey("a123").parseClaimsJws(token);
    claims = jws.getBody();
    System.out.println(claims);

}

10_单点登录SSO


自定义claims
@Test
public void claimsJwt() {
    //过期时间当前时间+10s
    long exp = System.currentTimeMillis() + 10 * 1000;

    //生成token
    String token = Jwts.builder()
        .setId("1234")
        .setSubject("admin")
        .setIssuedAt(new Date()) //当前时间
        .setExpiration(new Date(exp)) // 过期时间
        .claim("key1", "123")  //自定义claims
        .claim("key2", "abc")
        .signWith(SignatureAlgorithm.HS256, "a123")//签名
        .compact();
    //读取token
    Jws<Claims> jws = Jwts.parser().setSigningKey("a123").parseClaimsJws(token);
    Claims claims = jws.getBody();
    System.out.println(claims);
}

10_单点登录SSO


密码加密

任何应用考虑到安全,绝不能明文的方式保存密码。密码应该通过哈希算法进行加密。 有很多标准的算法比如SHA 或者MD5,结合salt(盐)是一个不错的选择。 Spring Security 提供了BCrypt强哈希算法,可以用来加密密码

添加依赖 :

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-crypto</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

测试:

@Test
public void testBCrypt() {
    //使用BCrypt强哈希算法并加盐:效果与MD5+加盐相同
    String password = BCrypt.hashpw("123", BCrypt.gensalt());
    System.out.println(password);

    //检查密码
    //参数1:明文密码,参数2:加密之后的结果
    //这样即使每次加密后的结果不同[即使相同的密码存到数据库的数据也可能不一样],只要判断是否与明文密码相同即可
    boolean result = BCrypt.checkpw("123", "$2a$10$yxogUGLdYMx8BGxMWHri0.XYu8dnX.iyR5xS8ABrTOg3xhL4Ae4xu");
    System.out.println(result);
}

10_单点登录SSO


6、SSO实现

JWT认证流程:

  1. 用户提供用户名和密码登录
  2. 服务器校验用户是否正确,如正确,就返回token给客户端,此token可以包含用户信息
  3. 客户端存储token,可以保存在cookie或者local storage
  4. 客户端以后请求时,都要带上这个token,一般放在请求头中
  5. 服务器判断是否存在token,并且解码后就可以知道是哪个用户
  6. 服务器这样就可以返回该用户的相关信息了

JWT工作方式:

在用户进行认证登录时,登录成功后服务器会返回一个JWT给客户端;那这个JWT就是用户的凭证,以后到哪里去都要带上这个凭证token。尤其访问受保护的资源的时候,通常把JWT放在Authorization header中。要用 Bearer schema,如header请求头中:

Authorization: Bearer <token>

Bearer与 之间加空格

10_单点登录SSO


7、登录处理

改yml
# 配置生成token所需参数
jwt:
  config:
    key: abc123 # 生成token的密钥
    ttl: 30  # 过期时间
JWT工具类

提供生成token与解析token

package com.supergo.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: xj0927
 * @Description:
 * @Date Created in 2021-01-13 19:25
 * @Modified By:
 */
@ConfigurationProperties(prefix = "jwt.config") //从配置文件读取密钥与过期时间
@Component
public class JwtUtil {
    private String key; //密钥
    private long ttl;//过期时间

    //生成token
    public String createToken(String id, String subject, String role) {
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)//主题
                .setIssuedAt(new Date())//现在时间
                .signWith(SignatureAlgorithm.HS256, key);//密钥

        if (ttl > 0) {
            builder.setExpiration(new Date(System.currentTimeMillis() + ttl * 1000));//过期时间
        }
        return builder.compact();
    }

    //解析token
    public Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(token)
                .getBody();
    }

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public long getTtl() {
        return ttl;
    }
    public void setTtl(long ttl) {
        this.ttl = ttl;
    }
}
pojo
package com.supergo.pojo;

/**
 * @Author: xj0927
 * @Description: 接收用户输入的账户密码
 * @Date Created in 2021-01-13 19:43
 * @Modified By:
 */
public class UserInfo {
    private String username;
    private String password;

    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public UserInfo() {
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


Service

接口:

public interface UserService extends BaseService<User> {
    //登陆方法
    public HttpResult doLogin(UserInfo userInfo);
}

实现类:

@Service
public class UserImpl extends BaseServiceImpl<User> implements UserService {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public HttpResult doLogin(UserInfo userInfo) {
        //根据用户名查用户是否存在
        User user = new User();
        user.setUserName(userInfo.getUsername());

        List<User> list = userService.findByWhere(user);
        if (list == null || list.size() == 0) {
            return HttpResult.error(401, "用户名不存在");
        }

        //如果有结果,判断密码是否正确[此密码是经过BCrypt加密了的]
        User user1 = list.get(0);
        if (!BCrypt.checkpw(userInfo.getPassword(), user1.getPassword())) {
            return HttpResult.error(401, "密码不正确");
        }
        //密码正确:生成token
        //返回token
        String token = jwtUtil.createToken(user.getId() + "", user.getName(), "admin");
        return HttpResult.ok(token);
    }
}

Controller
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/doLogin")
    public HttpResult doLogin(@RequestBody UserInfo userInfo) {
        System.out.println(userInfo.getUsername());
        return userService.doLogin(userInfo);
    }
}

测试

输入:

POST http://localhost:9005/user/doLogin

10_单点登录SSO


8、身份校验

身份校验应该在不同的微服务中来进行身份校验。我们在supergo_manager_service中品牌微服务中进行身份校验

JWT传递的方法是在头信息中添加Authorization ,内容为Bearer+空格 +token

Authorization: Bearer <token>
改pom
<!--token依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
配置JwtUtil组件
package com.supergo.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Author: xj0927
 * @Description:
 * @Date Created in 2021-01-13 19:25
 * @Modified By:
 */
@ConfigurationProperties(prefix = "jwt.config") //从配置文件读取密钥与过期时间
@Component
public class JwtUtil {
    private String key; //密钥
    private long ttl;//过期时间

    //生成token
    public String createToken(String id, String subject, String role) {
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)//主题
                .setIssuedAt(new Date())//现在时间
                .signWith(SignatureAlgorithm.HS256, key);//密钥

        if (ttl > 0) {
            builder.setExpiration(new Date(System.currentTimeMillis() + ttl * 1000));//过期时间
        }
        return builder.compact();
    }

    //解析token
    public Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(token)
                .getBody();
    }

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public long getTtl() {
        return ttl;
    }
    public void setTtl(long ttl) {
        this.ttl = ttl;
    }
}

拦截器

在拦截器中进行身份认证

package com.supergo.manager.interceptor;

import com.supergo.manager.exception.AuthException;
import com.supergo.manager.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author: xj0927
 * @Description: 配置拦截器进行身份验证
 * @Date Created in 2021-01-13 21:26
 * @Modified By:
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {


    @Autowired
    private JwtUtil jwtUtil; //注入对象

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("拦截器执行====>");

        String authHeader = request.getHeader("Authorization");
        if (authHeader == null) {
            throw new AuthException("权限不足");
        }
        if (!authHeader.startsWith("Bearer")) {
            throw new AuthException("权限不足");
        }
        //得到请求的token
        final String token = authHeader.substring(7);
        //解析token
        Claims claims = jwtUtil.parseToken(token);

        if (claims != null) {
            //TODO 权限相关操作
//            if ("admin".equals(claims.get("roles"))) {//如果是管理员
//                request.setAttribute("admin_claims", claims);
//            }
//            if ("user".equals(claims.get("roles"))) {//如果是用户
//                request.setAttribute("user_claims", claims);
//            }
        }

        //放行
        return true;
    }
}

配置拦截器
package com.supergo.manager.config;

import com.supergo.manager.interceptor.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author: xj0927
 * @Description: 配置拦截器
 * @Date Created in 2021-01-13 21:35
 * @Modified By:
 */
@Configuration
public class AuthConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;//拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor).addPathPatterns("/**");//拦截的请求
    }
}

全局异常处理

自定义异常:

package com.supergo.manager.exception;

/**
 * @Author: xj0927
 * @Description: 权限认证异常
 * @Date Created in 2021-01-13 21:29
 * @Modified By:
 */
public class AuthException extends Exception {

    public AuthException() {
    }

    public AuthException(String message) {
        super(message);
    }
}

异常处理:

package com.supergo.manager.exception;

import com.supergo.http.HttpResult;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Author: xj0927
 * @Description:
 * @Date Created in 2021-01-13 21:37
 * @Modified By:
 */
@ControllerAdvice  //全局异常
public class GlobalExceptionResolver {

    //token认证异常
    @ExceptionHandler(AuthException.class)
    @ResponseBody
    public HttpResult authExceptionResolver(AuthException e) {
        return HttpResult.error(401, e.getMessage());
    }


    //token过期异常
    @ExceptionHandler(ExpiredJwtException.class)
    @ResponseBody
    public HttpResult ExpiredJwtExceptionResolver(ExpiredJwtException e) {
        return HttpResult.error(401, "token已经过期");
    }

    //系统异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public HttpResult exceptionResolver(Exception e) {
        return HttpResult.error(401, e.getMessage());
    }
}

测试

正常请求:

10_单点登录SSO


不携带token:

10_单点登录SSO


token过期:

10_单点登录SSO


上一篇:单点登录系统原理与实现,图文并茂


下一篇:单点登录(SSO)看这一篇就够了