前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >10_单点登录SSO

10_单点登录SSO

作者头像
全栈程序员站长
发布2022-09-12 21:10:27
8480
发布2022-09-12 21:10:27
举报

大家好,又见面了,我是你们的朋友全栈君。

是什么

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

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


Cookie-Session登录

在这里插入图片描述
在这里插入图片描述

我们在浏览器(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。

在这里插入图片描述
在这里插入图片描述

基于token的认证登录

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

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

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

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

在这里插入图片描述
在这里插入图片描述

是什么

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数据结构应该如下

img
img

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

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

即:Header.Payload.Signature

对token进行Base64解码:


Header
代码语言:javascript
复制
{ 
   
	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),按照下面的公式产生签名。

代码语言:javascript
复制
  HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

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


单点登录微服务

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

1、建Module:supergo_sso
2、改pom
代码语言:javascript
复制
<?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
代码语言:javascript
复制
<?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、启动类
代码语言:javascript
复制
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
代码语言:javascript
复制
# 端口
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创建
代码语言:javascript
复制
@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);
}
在这里插入图片描述
在这里插入图片描述

token解析

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

将生成的token进行解析:

在这里插入图片描述
在这里插入图片描述

代码实现:

代码语言:javascript
复制
@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);
}
在这里插入图片描述
在这里插入图片描述

token过期校验

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

代码语言:javascript
复制
@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);

}
在这里插入图片描述
在这里插入图片描述

自定义claims
代码语言:javascript
复制
@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);
}
在这里插入图片描述
在这里插入图片描述

密码加密

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

添加依赖 :

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-crypto</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

测试:

代码语言:javascript
复制
@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);
}
在这里插入图片描述
在这里插入图片描述

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请求头中:

代码语言:javascript
复制
Authorization: Bearer <token>

Bearer与 之间加空格

在这里插入图片描述
在这里插入图片描述

7、登录处理
改yml
代码语言:javascript
复制
# 配置生成token所需参数
jwt:
  config:
    key: abc123 # 生成token的密钥
    ttl: 30 # 过期时间
JWT工具类

提供生成token与解析token

代码语言:javascript
复制
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
代码语言:javascript
复制
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

接口:

代码语言:javascript
复制
public interface UserService extends BaseService<User> { 
   
    //登陆方法
    public HttpResult doLogin(UserInfo userInfo);
}

实现类:

代码语言:javascript
复制
@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
代码语言:javascript
复制
@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);
    }
}

测试

输入:

代码语言:javascript
复制
POST http://localhost:9005/user/doLogin
在这里插入图片描述
在这里插入图片描述

8、身份校验

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

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

代码语言:javascript
复制
Authorization: Bearer <token>
改pom
代码语言:javascript
复制
<!--token依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
配置JwtUtil组件
代码语言:javascript
复制
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;
    }
}

拦截器

在拦截器中进行身份认证

代码语言:javascript
复制
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;
    }
}

配置拦截器
代码语言:javascript
复制
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("/**");//拦截的请求
    }
}

全局异常处理

自定义异常:

代码语言:javascript
复制
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);
    }
}

异常处理:

代码语言:javascript
复制
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());
    }
}

测试

正常请求:

在这里插入图片描述
在这里插入图片描述

不携带token:

在这里插入图片描述
在这里插入图片描述

token过期:

在这里插入图片描述
在这里插入图片描述

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152823.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 是什么
  • Cookie-Session登录
    • Cookie-Session解决单点登录
    • 基于token的认证登录
      • 是什么
        • 应用场景
          • 优缺点
            • 使用方法
              • 结构
          • 单点登录微服务
            • 单点登录微服务父项目:supergo_sso
              • 1、建Module:supergo_sso
              • 2、改pom
            • 单点登录微服务提供者:supergo_sso_service
              • 1、建Module:supergo_sso_service
              • 2、改pom
              • 3、启动类
              • 4、建yml
              • 5、JWT测试
              • 6、SSO实现
              • 7、登录处理
              • 8、身份校验
          相关产品与服务
          访问管理
          访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档