JJWT实现令牌Token

news2024/11/24 13:41:45

登录实现方式

Session

详情:

https://www.cnblogs.com/andy-zhou/p/5360107.html

会话的概念

会话就好比打电话,一次通话可以理解为一次会话。我们登录一个网站,在一个网站上不同的页面浏览,最后退出这个网站,也是一个会话。一个网站对于不同的用户会展示出不同的页面信息,但是在HTTP协议中,客户端请求服务端是一种无状态的连接-每次请求都是独立的请求,服务器怎么分辨这些请求是哪个用户发出的请求呢?

如果不能分辨哪个用户发出的请求,就不知道该反馈哪个用户的信息回来,那试想我们登录网站之后,显示的都是别人的信息岂不乱套了。所以服务器需要知道发出请求的是谁,需要一个用户标识来保证会话的正常进行。

Session的概念

Session 是存放在服务器端的,类似于Session结构来存放用户数据,当浏览器 第一次发送请求时,服务器自动生成了一个Session和一个Session ID用来唯一标识这个Session,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的Session。可以自己匹配到对应用户的session

由于Session存放在服务器端,所以随着时间的推移或者用户访问的增多,会给服务器增加负担。使用的时候要考虑下服务器的性能。

Cookie的概念

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端会把Cookie保存起来。

由于Cookie是存放在客户端,是可见的,安全性就会很低。因此不建议将一些重要的信息放在cookie中。

使用

​ Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。

Session是用于单体应用的,JSP页面

Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
cookie.setSecure(true); // 设置安全属性
response.addCookie(cookie); // 输出到客户端

    @PostMapping("/login")
    public String login(HttpSession session, String username){
        session.setAttribute("user", username);
        return "页面";
    }

    @PostMapping("/update")
    public String update(HttpSession session, Model model, User user){
        // 可以自己匹配到对应用户的session
        String username = session.getAttribute("user");
        if(username!=null&&username.equals.(user.getUsername())){
            //说明用户有登陆
            int k = userService.update(user);
            if (k>0){
                // 获取用户信息
                user = userService.select(user.getUsername());
                model.addAttribute("user", user);
                return "成功页面";
            }
        }
        return "失败页面";
    }

// HttpServletRequest可作为controller
HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session=request.getSession();//达到session对象

Token

获取到令牌后,每次请求资源访问时都需要带token回来验证合法性和是否有权限。token一般包含用户的信息和权限集。实现token的框架有Spring Security oAuth2、Apache Shrio、JWT。

jwt令牌

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

说白了就是封装了用户的身份信息,给各个微服务识别用户身份。

JWT的构成

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

{"typ":"JWT","alg":"HS256"}

在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

小知识:Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。JDK 中提供了非常方便的 BASE64EncoderBASE64Decoder,用它们可以非常方便的完成基于 BASE64 的编码和解码

载荷内容(playload)

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

(1)标准中注册的声明(建议但不强制使用)(参与令牌校验)

iss: jwt签发者
sub: 当前令牌的描述说明
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

(2)公共的声明(不参与令牌校验)

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

(3)私有的声明(不参与令牌校验)

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

这个指的就是自定义的claim。比如下面面结构举例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:JWT规定的claim,JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证(还不知道是否能够验证);而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行。

定义一个payload:

{"sub":"1234567890","name":"John Doe","admin":true}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature)

jwt的第三部分是一个签证信息(效验数据是否被篡改),这个签证信息由三部分组成:

header (base64后的)

payload (base64后的)

secret(盐)

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式HS256进行加盐secret组合加密,然后就构成了jwt的第三部分。

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。签证不能被破解。

JJWT创建令牌

JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

官方文档:

https://github.com/jwtk/jjwt

创建TOKEN

(1)依赖引入

<!--鉴权-->
<jjwt.version>0.9.0</jjwt.version>
<!--鉴权-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt.version}</version>
</dependency>

(2)创建测试

/test/java下创建测试类,并设置测试方法。

有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间。

public class JwtTest {

    /****
     * 创建Jwt令牌
     */
    @Test
    public void testCreateJwt(){
        JwtBuilder builder= Jwts.builder()
                .setId("888")             //设置唯一编号
                .setIssuer("吴")         //颁发者
                .setSubject("小白")        //设置主题  可以是JSON数据
                .setIssuedAt(new Date())  //设置签发日期
                .setExpiration(new Date(System.currentTimeMills()+3600000))//令牌过期时间,这里1小时
                .signWith(SignatureAlgorithm.HS256,"itcast");//设置签名 使用HS256算法,并设置SecretKey(字符串)
        
        //自定义载荷
        Map<String,Object> userInfo = new HashMap<String,Object>();
        userInfo.put("company","**公司");
        userInfo.put("address","深圳");
        builder.addClaims(userInfo);
        
        //构建 并返回一个字符串
        String token = builder.compact()
        System.out.println(token);
    }
}

运行打印结果:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjIyODd9.RBLpZ79USMplQyfJCZFD2muHV_KLks7M1ZsjTu6Aez4

再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间。

解析TOKEN

我们刚才已经创建了token ,在web应用中这个操作是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),那服务端接到这个token应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果。

/***
 * 解析Jwt令牌数据
 */
@Test
public void testParseJwt(){
    String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjIyODd9.RBLpZ79USMplQyfJCZFD2muHV_KLks7M1ZsjTu6Aez4";
    Claims claims = Jwts.parser().
            setSigningKey("itcast"). //盐
            parseClaimsJws(token). // 要解析的令牌对象
            getBody(); // 获取解密后的数据
    System.out.println(claims);
    System.out.println(claims.toStrig());
}

运行打印效果:

{jti=888, sub=小白, iat=1562062287}

试着将token或签名秘钥篡改一下,会发现运行时就会报错,所以解析token也就是验证token.

如果,令牌过期则报错如下:

在这里插入图片描述

当前时间超过过期时间,则会报错。

自定义claims

我们刚才的例子只是存储了id和subject两个信息,如果你想存储更多的信息(例如角色)可以定义自定义claims。

创建测试类,并设置测试方法:

创建token:

在这里插入图片描述

运行打印效果:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjMyOTIsImFkZHJlc3MiOiLmt7HlnLPpu5Hpqazorq3nu4PokKXnqIvluo_lkZjkuK3lv4MiLCJuYW1lIjoi546L5LqUIiwiYWdlIjoyN30.ZSbHt5qrxz0F1Ma9rVHHAIy4jMCBGIHoNaaPQXxV_dk
/***
 * 解析Jwt令牌数据
 */
@Test
public void testParseJwt(){
    String compactJwt="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NjIwNjMyOTIsImFkZHJlc3MiOiLmt7HlnLPpu5Hpqazorq3nu4PokKXnqIvluo_lkZjkuK3lv4MiLCJuYW1lIjoi546L5LqUIiwiYWdlIjoyN30.ZSbHt5qrxz0F1Ma9rVHHAIy4jMCBGIHoNaaPQXxV_dk";
    Claims claims = Jwts.parser().
            setSigningKey("itcast").
            parseClaimsJws(compactJwt).
            getBody();
    System.out.println(claims);
}

运行效果:

在这里插入图片描述

鉴权处理

思路分析

在这里插入图片描述

1.用户通过访问微服务网关调用微服务,同时携带头文件信息(令牌一般放请求头,或者cookie、参数)
2.在微服务网关这里进行拦截,拦截后获取用户要访问的路径
3.识别用户访问的路径是否需要登录,如果需要,识别用户的身份是否能访问该路径[这里可以基于数据库设计一套权限]
4.如果需要权限访问,用户已经登录,则放行
5.如果需要权限访问,且用户未登录,则提示用户需要登录
6.用户通过网关访问用户微服务,进行登录验证
7.验证通过后,用户微服务会颁发一个令牌给网关,网关会将用户信息封装到头文件中,并响应用户
8.用户下次访问,携带头文件中的令牌信息即可识别是否登录

用户登录签发TOKEN工具类

(1)生成令牌工具类

在commons或gateway微服务中创建类JwtUtil,主要辅助生成Jwt令牌信息,代码如下:

package com.changgou.commons.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * Title:JJWT生成令牌工具类
 * Description:
 * @author WZQ
 * @version 1.0.0
 * @date 2020/3/10
 */
public class JwtUtil {
    //默认有效期
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一个小时

    //Jwt令牌信息
    public static final String JWT_KEY = "itcast";

    /**
     * 生成令牌
     * @param id 唯一标识
     * @param subject 主题
     * @param ttlMillis 令牌过期时间
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        //指定算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //当前系统时间
        long nowMillis = System.currentTimeMillis();
        //令牌签发时间
        Date now = new Date(nowMillis);

        //如果令牌有效期为null,则默认设置有效期1小时
        if (ttlMillis == null) {
            ttlMillis = JwtUtil.JWT_TTL;
        }

        //令牌过期时间设置
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);

        //生成秘钥
        SecretKey secretKey = generalKey();

        //封装Jwt令牌信息
        JwtBuilder builder = Jwts.builder()
                .setId(id)                    //唯一的ID
                .setSubject(subject)          // 主题  可以是JSON数据
                .setIssuer("admin")          // 签发者
                .setIssuedAt(now)             // 签发时间
                .signWith(signatureAlgorithm, secretKey) // 签名算法以及密匙
                .setExpiration(expDate);      // 设置过期时间
        return builder.compact();
    }

    /**
     * 生成加密 secretKey
     * 盐base64加密成新的盐
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }


    /**
     * 解析令牌数据
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

    public static void main(String[] args) {
        String jwt = JwtUtil.createJWT("weiyibiaoshi", "aaaaaa", null);
        System.out.println(jwt);
        try {
            Claims claims = JwtUtil.parseJWT(jwt);
            System.out.println(claims);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

//exp获取的是时间戳,解析出来是Date,过期时间

(2) 用户登录成功 则签发TOKEN,修改登录的方法:

如果报错,尝试在该微服务也导入jjwt的依赖

代码如下:

/***
 * 用户登录
 */
@RequestMapping(value = "/login")
public ResponseResult login(String username,String password,HttpServletResponse response){
    //查询用户信息
    User user = userService.findById(username);

    if(user!=null && BCrypt.checkpw(password,user.getPassword())){
        //设置令牌信息
        Map<String,Object> info = new HashMap<String,Object>();
        info.put("role","USER");
        info.put("success","SUCCESS");
        info.put("username",username);
        //生成令牌,过期日数不设置,默认
        String token = JwtUtil.createJWT(UUID.randomUUID().toString(), JSON.toJSONString(info),null);
        //IdUtil.simpleUUID(),hutool工具UUID去-
        
        //令牌存入cookie
        Cookie cookie = new Cookie("Authorization",token);
        cookie.setDomain("localhost");
        cookie.setPath("/");
        response.addCookie(cookie);
        
        //或者直接给前端处理,请求头
        return new ResponseResult(true,StatusCode.OK,"登录成功!",token);
    }
    return  new ResponseResult(false,StatusCode.LOGINERROR,"账号或者密码错误!");
}


    /**
     * token验证过期
     * @param tokenMap
     * @return
     */
    @PostMapping("/token/verify")
    @ApiOperation(value = "token验证过期")
    public ResponseResult<String> tokenVerify(@RequestBody Map<String,String> tokenMap) throws Exception {
        if (!StringUtils.isEmpty(tokenMap.get("token"))){

            Claims claims = JwtUtil.parseJWT(tokenMap.get("token"));

            //获取的是时间戳,解析出来是Date,过期时间
            Date expiration = claims.getExpiration();

            if (expiration.before(new Date())){
                return new ResponseResult<String>(ResponseResult.CodeStatus.OK,"需要重新登录","1");
            }

            return new ResponseResult<String>(ResponseResult.CodeStatus.OK,"不需要重新登录","2");
        }
        return new ResponseResult<String>(ResponseResult.CodeStatus.FAIL,"token为空",null);
    }

自定义全局过滤器

结合微服务网关实现令牌授权

拷贝JwtUtil到gateway-web网关微服务中,最好把JwtUtil拷贝到微服务网关中,导入commons到网关中可能报错,含有大量依赖。

创建过滤器类,如图所示:

在这里插入图片描述

AuthorizeFilter代码如下:

import com.changgou.gateway.web.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Title:全局过滤器类
 * Description:
 * @author WZQ
 * @version 1.0.0
 * @date 2020/3/10
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    //令牌头名字
    private static final String AUTHORIZE_TOKEN = "Authorization";

    /***
     * 全局过滤器
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取Request、Response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取请求的URI
        String path = request.getURI().getPath();

        //1.获取头文件中的令牌信息
        String tokent = request.getHeaders().getFirst(AUTHORIZE_TOKEN);

        //2.如果头文件中没有,则从请求参数中获取
        if (StringUtils.isEmpty(tokent)) {
            tokent = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
        }

        //3.都没有则从cookie中获取
        if (StringUtils.isEmpty(tokent)) {
            HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            if (cookie!=null){
                tokent = cookie.getValue();
            }
        }

        //如果全为空,没有令牌,则拦截
        if (StringUtils.isEmpty(tokent)) {
            //设置没有权限的状态码,401没有权限
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //返回
            return response.setComplete();
        }

        //有令牌
        //解析令牌数据
        try {
            Claims claims = JwtUtil.parseJWT(tokent);
        } catch (Exception e) {
            e.printStackTrace();
            //解析失败,响应401错误,没有权限
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //空数据
            return response.setComplete();
        }

        //放行
        return chain.filter(exchange);
    }


    /***
     * 过滤器执行顺序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

配置过滤规则

修改网关系统的yml文件:

根据业务过滤对应的路径

          routes:
            - id: changgou_goods_route
              uri: lb://goods
              predicates:
              - Path=/api/album/**,/api/brand/**,/api/cache/**,/api/categoryBrand/**,/api/category/**,/api/para/**,/api/pref/**,/api/sku/**,/api/spec/**,/api/spu/**,/api/stockBack/**,/api/template/**
              filters:
              - StripPrefix=1
              - name: RequestRateLimiter #请求数限流 名字不能随便写 ,使用默认的facatory
                args:
                  key-resolver: "#{@ipKeyResolver}"
                  redis-rate-limiter.replenishRate: 1
                  redis-rate-limiter.burstCapacity: 1
            #用户微服务
            - id: changgou_user_route
              uri: lb://user
              predicates:
              - Path=/api/user/**,/api/address/**,/api/areas/**,/api/cities/**,/api/provinces/**
              filters:
              - StripPrefix=1

参考官方手册:

https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html#_stripprefix_gatewayfilter_factory

会话保持

用户每次请求的时候,我们都需要获取令牌数据,方法有多重,可以在每次提交的时候,将数据提交到头文件中,也可以将数据存储到Cookie中,每次从Cookie中校验数据,还可以每次将令牌数据以参数的方式提交到网关,这里面采用Cookie的方式比较容易实现。

可以获取用户ip放到token里面,每次登录校验ip,不同ip登录得去掉上一次的登录状态。

登录封装Cookie

修改user微服务,每次登录的时候,添加令牌信息到Cookie中,看情况添加,代码如下:

/***
 * 用户登录
 */
@RequestMapping(value = "/login")
public ResponseResult login(String username,String password,HttpServletResponse response){
    //查询用户信息
    User user = userService.findById(username);

    if(user!=null && BCrypt.checkpw(password,user.getPassword())){
        //设置令牌信息
        Map<String,Object> info = new HashMap<String,Object>();
        info.put("role","USER");
        info.put("success","SUCCESS");
        info.put("username",username);
        //生成令牌
        String token = JwtUtil.createJWT(UUID.randomUUID().toString(), JSON.toJSONString(info),null);
        
        //令牌存入cookie
        Cookie cookie = new Cookie("Authorization",token);
        cookie.setDomain("localhost");
        cookie.setPath("/");
        response.addCookie(cookie);
        
        //或者直接给前端处理,请求头
        return new ResponseResult(true,StatusCode.OK,"登录成功!",token);
    }
    return  new ResponseResult(false,StatusCode.LOGINERROR,"账号或者密码错误!");
}

过滤器获取令牌数据

每次在网关中通过过滤器获取Cookie中的令牌,然后对令牌数据进行解析,修改微服务网关gateway中的全局过滤器AuthorizeFilter,代码如下:

        //3.都没有则从cookie中获取
        if (StringUtils.isEmpty(tokent)) {
            HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            if (cookie!=null){
                tokent = cookie.getValue();
                hasToken = false;
            }
        }

登录后测试,可以识别用户身份,不登录无法识别。如下访问http://localhost:8001/api/user会携带令牌数据:

在这里插入图片描述

添加Header信息

我们还可以在Gateway的全局过滤器中添加请求头信息,例如可以将令牌信息添加到请求头中,在资源微服务中获取头信息,如下代码:

修改微服务网关中的AuthorizeFilter过滤器,在令牌信息校验那块将令牌加入到请求头中,如下代码:

        //是在请求头,则true
        boolean hasToken = true;

        //1.获取头文件中的令牌信息
        String tokent = request.getHeaders().getFirst(AUTHORIZE_TOKEN);

        //2.如果头文件中没有,则从请求参数中获取
        if (StringUtils.isEmpty(tokent)) {
            tokent = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
            hasToken = false;
        }

        //3.都没有则从cookie中获取
        if (StringUtils.isEmpty(tokent)) {
            HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            if (cookie!=null){
                tokent = cookie.getValue();
                hasToken = false;
            }
        }

        //不是从请求头中拿到的token
        if (!hasToken){
            //将令牌封装到头文件中,让其访问其他资源服务器
            request.mutate().header(AUTHORIZE_TOKEN,tokent);
        }

在user微服务的UserController的findAll方法中获取请求头测试,代码如下:

在这里插入图片描述

后台输出令牌数据如下:

在这里插入图片描述

网关过滤器全部代码

package com.changgou.gateway.web.filter;

import com.changgou.gateway.web.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Title:全局过滤器类
 * Description:
 * @author WZQ
 * @version 1.0.0
 * @date 2020/3/10
 */
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    //令牌头名字
    private static final String AUTHORIZE_TOKEN = "Authorization";

    /***
     * 全局过滤器
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取Request、Response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取请求的URI
        String path = request.getURI().getPath();
        
        //如果是登录,注册等请求,一律放行
        if(URLFilter.hasAuthorize(path)){
            //放行
            return chain.filter(exchange);
        }

        //不在头文件中,则在这里封装到头文件中,资源服务器才可以访问,如果结合spring security的话,
        //都是放在请求头
        boolean hasToken = true;

        //1.获取头文件中的令牌信息
        String tokent = request.getHeaders().getFirst(AUTHORIZE_TOKEN);

        //2.如果头文件中没有,则从请求参数中获取
        if (StringUtils.isEmpty(tokent)) {
            tokent = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
            hasToken = false;
        }

        //3.都没有则从cookie中获取
        if (StringUtils.isEmpty(tokent)) {
            HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            if (cookie!=null){
                tokent = cookie.getValue();
                hasToken = false;
            }
        }

        //如果全为空,没有令牌,则拦截
        if (StringUtils.isEmpty(tokent)) {
            //设置没有权限的状态码,401没有权限
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //返回
            return response.setComplete();
        }

        //看情况,可能需要统一加上“bearer ”,cookie无法加
        //如果全为空,没有令牌,则拦截
        if (StringUtils.isEmpty(tokent)) {
            //设置没有权限的状态码,401没有权限
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //返回
            return response.setComplete();
        }else{
            //有令牌
            //解析令牌数据
            //Claims claims = JwtUtil.parseJWT(tokent);
            //判断是否有前缀"bearer "
            if (!tokent.startsWith("bearer ") || !tokent.startsWith("Bearer ")){
                tokent = "bearer "+tokent;
            }
        }
        
        //请求头没有,添加进去
        if (!hasToken){
            //将令牌封装到头文件中,让其访问其他资源服务器
            request.mutate().header(AUTHORIZE_TOKEN,tokent);
        }

        //放行
        return chain.filter(exchange);
    }


    /***
     * 过滤器执行顺序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158104.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java AWT 图形界面编程】Container 容器 ② ( Frame 窗口示例 | Panel 示例 | 窗口中文乱码处理 )

文章目录一、Frame 窗口示例二、Panel 示例三、窗口中文乱码处理一、Frame 窗口示例 首先 , 创建 Frame 实例对象 , 该对象就是 操作系统中应用软件的 窗口 ; // 1. 创建窗口 Frame frame new Frame("AWT 图形界面编程");Frame 是 Window 的子类 , public class F…

前端号外—2022年最受欢迎居然是它,Node.js危已

导读 | 2022年是艰难的一年&#xff0c;不仅有互联网的寒冬、还有新冠疫情的洗礼。但是似乎这一切都阻挡不了JavaScript的内卷&#xff0c;一年不长不短的时间中&#xff0c;JavaScript从创新、性能、功能等多维度深度进化&#xff0c;给前端带来了诸多惊喜。本文基于github上流…

仓库管理系统demo搭建指南

1、简介 1.1、案例简介 本文将介绍&#xff0c;如何搭建云进销存-仓库管理。 1.2、应用场景 云进销存-仓库管理应用分仓管理并提供灵活的库存计算方式及库存预警。 2、设置方法 2.1、表单搭建 1&#xff09;新建表单【商品管理】&#xff0c;字段设置如下&#xff1a; …

TensorFlow笔记之神经网络完成多分类任务

文章目录前言一、数据集调用二、Tensorflow1.x1.单隐藏层2.模型保存与调用三、Tensorflow2.x1.全连接层类2.keras建模总结前言 对TensorFlow笔记之单神经元完成多分类任务进行修改&#xff0c;在tf1.x与tf2.x中使用神经网络完成手写体数字识别多分类任务。 一、数据集调用 数…

10、Javaweb_Cookkie会话Session修改IDEA代码模板

修改IDEA代码模板 选择Setting... 找到要修改的代码模板&#xff0c;点击ok修改即可 使用模板创建方法 ,点击文件包,右键New选择文件类型 点击ok即可 创建完成 会话技术 1. 会话&#xff1a;一次会话中包含多次请求和响应。 * 一次会话&#xff1a;浏览器第一次给服务器资…

JAVA开发(web常见安全漏洞以及修复建议)

web安全常见漏洞修复建议&#xff1a;SQL注入规避 代码层最佳防御sql漏洞方案&#xff1a;使用预编译sql语句查询和绑定变量。&#xff08;1&#xff09;使用预编译语句&#xff0c;使用PDO需要注意不要将变量直接拼接到PDO语句中。所有的查询语句都使用数据库提供的参数化查询…

92、【树与二叉树】leetcode ——222. 完全二叉树的节点个数:普通二叉树求法+完全二叉树性质求法(C++版本)

题目描述 原题链接&#xff1a;222. 完全二叉树的节点个数 解题思路 1、普通二叉树节点个数求法 &#xff08;1&#xff09;迭代&#xff1a;层序遍历BFS 遍历一层获取一层结点 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode …

华为交换机、路由器设备批量配置端口方法步骤

华为交换机、路由器批量配置端口方法步骤 在现实工作中&#xff0c;如果要对多个端口做同样的配置&#xff0c;每个接口逐一进行相同的配置&#xff0c;很容易出错&#xff0c;而且造成大量重复工作。 配置端口组功能就可以解决这个问题啦。 你只需要将这些以太网接口加入同一…

HTML学习笔记(全)

HTML 文章目录HTML第一章——HTML 基础认识1. 1 基础补充1.1.1 网页组成1.1.2 代码如何转换成网页1.1.3 渲染引擎&#xff08;了解&#xff09;1.1.4 web 标准1.2 HTML 基础认知1. HTML的概念2. HTML页面固定结构3. **标签说明&#xff1a;**第二章——HTML基础语法2.1——注释…

国产linux操作系统——麒麟操作系统的来龙去脉

文章目录1、linux操作系统2、国产操作系统3、麒麟操作系统4、引用1、linux操作系统 目前市场主流的linux操作系统分类大致如此&#xff0c;国产操作系统的麒麟操作系统&#xff0c;底层比较杂&#xff0c;所以单独一类。 2、国产操作系统 排名日期截止到2022 这里提一下排名第…

科技云报道:从re:Invent 2022读懂亚马逊云科技的“生态棋局”

科技云报道原创。 懂棋的人都知道&#xff0c;下棋靠的是智力的角逐&#xff0c;也是气度的较量。 到了云计算发展的新时期&#xff0c;下棋的“人”已经变了&#xff0c;单靠一个人的智力解决不了N个用户的N种问题。 因此&#xff0c;近年来头部云厂商纷纷加大了对合作伙伴生…

centos7:jenkins+nodejs前端自动化部署

系统:centos7 nodejs版本&#xff1a;v16.18.1 npm版本&#xff1a;8.19.2 由于centos7最大只支持16.18.1版本&#xff0c;尽量让前端写代码时使用这个版本&#xff0c;linux系统如果要装高版本的node需要安装glibc库&#xff0c;很危险&#xff0c;尽量不要操作。 jenkin…

Hudi系列6:使用pyspark操作Hudi

文章目录前言一. pyspark连接hudi二. 创建表三. 插入数据四. 查询数据五. Time Travel查询六. 更新数据七. 增量查询八. 基于时间点查询九. 删除数据9.1 软删除9.2 硬删除十. 插入覆盖十一. Spark其它命令11.1 Alter Table11.2 Partition SQL Command参考:前言 软件版本Python…

低成本MEMS惯导系统的捷联惯导解算MATLAB仿真

低成本MEMS惯导系统的捷联惯导解算MATLAB仿真一、姿态角转换为四元数二、四元数转换为姿态角三、反对称阵四、位置更新五、姿态更新六、程序及数据主程序&#xff1a;子程序&#xff1a;数据及完整程序之前将高成本的捷联惯导忽略地球自转、圆锥曲线运动以及划桨运动等化简为可…

【学习笔记之Linux】工具之make/Makefile与git

make/Makefile&#xff1a; 背景知识&#xff1a; 一个工程中的源文件不计数&#xff0c;按类型、功能、模块分别放在若干个目录中&#xff0c;Makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件需要后编译&#xff0c;那些文件需要重新编…

电源《龙珠超:超级人造人》观后感

上周看了动画电影《龙珠超&#xff1a;超级人造人》&#xff0c;《龙珠》这个系列同《火影》、《死神》、《海贼王》和《名侦探柯南》等都存在了很长时间&#xff0c;不断在更新&#xff0c;都是非常好的IP,伴随着很多人走过童年&#xff0c;也是因为时间太长了&#xff0c;记得…

品牌打假,假货治理,有什么好的方法

品牌打假&#xff0c;清除渠道假货&#xff0c;可以提高消费者对品牌的满意度与忠诚度&#xff0c;增强经销商的经销信心&#xff0c;维护稳定的价格体系及经销体系&#xff0c;树立良好的品牌形象。 但是品牌在打假的过程中&#xff0c;由于经验、时间、方法、技术等方面的局…

测试开发 | 接口测试之HTTP 协议讲解

本文节选自霍格沃兹测试开发学社内部教材HTTP 协议是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。客户端向服务端发送 HTTP 请求&#xff0c;服务端则会在响应中返回所请求的数据。了解了 HTTP 协议&#xff0c;才能对接口测试进行更…

sql实现字段分割一行转多行的示例代码

先看一下数据结构&#xff0c;我这里字段比较少&#xff0c;只弄了最重要的部分 根据我们上次学到的LEFT()函数进行分组 SELECT LEFT(provinces,6),COUNT(1) FROM region_map_copy GROUP BY LEFT(provinces,6) 得到的结果如下&#xff1a; 这样的效果并不是我们想要的&#x…

必贝特科创板IPO过会:预计2025年前实现商业化,钱长庚为实控人

2023年1月10日&#xff0c;上海证券交易所披露的信息显示&#xff0c;广州必贝特医药股份有限公司&#xff08;下称“必贝特”&#xff09;获得上市委会议审核通过。据贝多财经了解&#xff0c;必贝特于2022年6月29日在科创板递交上市申请。 公开信息显示&#xff0c;必贝特是一…