JSON Web令牌(JWT)详解

2023-11-02

前言

今天要分享的知识是JWT

码字不易,转载请说明!!!


目录

一、JWT出现的原因及工作原理

JWT是什么

为什么使用JWT

JWT的工作原理  

JWT组成

传统开发对资源的访问限制利用session完成图解 

​JWT所解决的问题及机制

JWT解决不需要登录就能直接访问的问题 

web.xml:解决JWT问题的过滤器

JwtFilter.java:开启jwt令牌验证功能 

UserAction .java:将jwt令牌塞入响应头 

未登录就不能显示内容

并且报出403错误

解决方案 

①在前台获取JWT令牌:src/api/http.js

②main.js内进行赋值:src/main.js  

③将JWT令牌这个值放到vuex内:src/store/Mutations.js 

④定义变量:src/store/State.js

⑤请求拦截器:src/api/http.js 

⑥定义获取的方法:src/store/getters.js 

⑦总结

二、JWT工具类介绍,三种场景

JWT的数据结构

JwtUtils.java

三种场景

①如何通过jwt的工具类去生成令牌以及解析令牌

​②令牌过期的场景

③延续令牌的存活时间


一、JWT出现的原因及工作原理

JWT是什么

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方法

为什么使用JWT

 JWT的精髓在于:“去中心化”,数据是保存在客户端的

JWT的工作原理  

  1. 是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,示例如下:
      {"UserName": "Chongchong","Role": "Admin","Expire": "2018-08-08 20:15:56"}

   2. 之后,当用户与服务器通信时,客户在请求中发回JSON对象
    
   3. 为了防止用户篡改数据,服务器将在生成对象时添加签名,并对发回的数据进行验证

JWT组成

 一个JWT实际上就是一个字符串,它由三部分组成:

   头部(Header)、载荷(Payload)与签名(signature)

传统开发对资源的访问限制利用session完成图解 

JWT所解决的问题及机制

JWT解决不需要登录就能直接访问的问题 

web.xml:解决JWT问题的过滤器

<!-- 解决jwt问题的过滤器 -->
    <filter>
        <filter-name>jwtFilter</filter-name>
        <filter-class>com.zking.vue.util.JwtFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>jwtFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

JwtFilter.java:开启jwt令牌验证功能 

package com.zking.vue.util;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.jsonwebtoken.Claims;

/**
 * * JWT验证过滤器,配置顺序 :CorsFilte-->JwtFilter-->struts2中央控制器
 * 
 * @author Administrator
 *
 */
public class JwtFilter implements Filter {

    // 排除的URL,一般为登陆的URL(请改成自己登陆的URL)
    private static String EXCLUDE = "^/vue/userAction_login\\.action?.*$";

    private static Pattern PATTERN = Pattern.compile(EXCLUDE);

    private boolean OFF = false;// true关闭jwt令牌验证功能

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        String path = req.getServletPath();
        if (OFF || isExcludeUrl(path)) {// 登陆直接放行
            chain.doFilter(request, response);
            return;
        }

        // 从客户端请求头中获得令牌并验证
        String jwt = req.getHeader(JwtUtils.JWT_HEADER_KEY);
        Claims claims = this.validateJwtToken(jwt);
        if (null == claims) {
            // resp.setCharacterEncoding("UTF-8");
            resp.sendError(403, "JWT令牌已过期或已失效");
            return;
        } else {
            String newJwt = JwtUtils.copyJwt(jwt, JwtUtils.JWT_WEB_TTL);
            resp.setHeader(JwtUtils.JWT_HEADER_KEY, newJwt);
            chain.doFilter(request, response);
        }
    }

    /**
     * 验证jwt令牌,验证通过返回声明(包括公有和私有),返回null则表示验证失败
     */
    private Claims validateJwtToken(String jwt) {
        Claims claims = null;
        try {
            if (null != jwt) {
                claims = JwtUtils.parseJwt(jwt);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    /**
     * 是否为排除的URL
     * 
     * @param path
     * @return
     */
    private boolean isExcludeUrl(String path) {
        Matcher matcher = PATTERN.matcher(path);
        return matcher.matches();
    }

    // public static void main(String[] args) {
    // String path = "/sys/userAction_doLogin.action?username=zs&password=123";
    // Matcher matcher = PATTERN.matcher(path);
    // boolean b = matcher.matches();
    // System.out.println(b);
    // }

}

UserAction .java:将jwt令牌塞入响应头 

package com.zking.vue.web;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.opensymphony.xwork2.ModelDriven;
import com.zking.base.web.BaseAction;
import com.zking.vue.biz.UserBiz;
import com.zking.vue.entity.User;
import com.zking.vue.util.JsonData;
import com.zking.vue.util.JwtUtils;
import com.zking.vue.util.PageBean;
import com.zking.vue.util.ResponseUtil;
import com.zking.vue.util.StringUtils;

public class UserAction extends BaseAction implements ModelDriven<User>{
    private UserBiz userBiz;
    private User user = new User();

    public UserBiz getUserBiz() {
        return userBiz;
    }

    public void setUserBiz(UserBiz userBiz) {
        this.userBiz = userBiz;
    }
     
    public String login() {
        ObjectMapper om = new ObjectMapper();
        JsonData jsonData = null;
        try {
            if(StringUtils.isBlank(user.getUname()) || StringUtils.isBlank(user.getPwd())) {
                jsonData = new JsonData(0, "用户或者密码为空", user);
            }else {
                User u = this.userBiz.login(user);
                Map<String, Object> claims = new HashMap<String, Object>();
                claims.put("uname",user.getUname());
                claims.put("pwd", user.getPwd());
                String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
                response.setHeader(JwtUtils.JWT_HEADER_KEY, jwt);
                jsonData = new JsonData(1, "登录成功", u);
            }
        } catch (Exception e) {
            e.printStackTrace();
            jsonData = new JsonData(0, "用户或者密码错误", user);
        }finally {
            try {
                ResponseUtil.write(response, om.writeValueAsString(jsonData));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        return null;
    }
    
    public String getAsyncData() {
        ObjectMapper om = new ObjectMapper();
        try {
            Thread.sleep(6000);
            ResponseUtil.write(response, om.writeValueAsString("http://www.javaxl.com"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public User getModel() {
        return user;
    }
}

未登录就不能显示内容

并且报出403错误

403错误出现的原因:

    ①被限制了,限制了访问,没有拿到凭证

    ②做爬虫的时候也会出现,IP被封了,没有凭证

    ③被拉入黑名单了

解决方案 

①在前台获取JWT令牌:src/api/http.js

// 响应拦截器
axios.interceptors.response.use(function(response) {
    debugger;
    var jwt = response.headers['jwt'];
    if(jwt){
        window.vm.$store.commit('setJwt',{jwt:jwt});
    }
    return response;
}, function(error) {
    return Promise.reject(error);
});

②main.js内进行赋值:src/main.js  

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
//process.env.MOCK && require('@/mock')
import 'element-ui/lib/theme-chalk/index.css' // 新添加 2 
import App from './App'
import router from './router'
import ElementUI from 'element-ui' // 新添加 1
import axios from '@/api/http' // #vue项目对axios的全局配置     
//import axios from 'axios' 
import VueAxios from 'vue-axios'
import store from './store'

Vue.use(ElementUI)   // 新添加 3
Vue.use(VueAxios,axios)
Vue.config.productionTip = false
/* eslint-disable no-new */
/* 绑定了index.html的边界 */
window.vm=new Vue({
  el: '#app',
  data(){
    return {
      Bus:new Vue({
      })
    }
  },
  router,
  store,
  components: { App },
  template: '<App/>'
})

③将JWT令牌这个值放到vuex内:src/store/Mutations.js 

export default{
   setResturantName:(state,payload) =>{
     state.resturantName = payload.resturantName;
   },
   setJwt:(state,payload) =>{
     state.jwt = payload.jwt;
   },
   doAjax:(state,payload)=>{
     //需求:想在当前的文件中与后台服务器做数据交互
     let _this = payload._this;
     let url = _this.axios.urls.SYSTEM_MENU_TREE;
     _this.axios.post(url,{}).then((resp) =>{
       console.log(resp);
     }).catch(function(error) {
       console.log(error);
     });

     /* //箭头函数解决了this指针污染问题
     this.$root.Bus.$on("collapsed-side",(v)=>{
         this.collapsed = v;
     }); */

   }
}

定义变量:src/store/State.js

export default{
    resturantName:'飞歌餐馆',
     jwt:''
}

请求拦截器:src/api/http.js 

// 请求拦截器
axios.interceptors.request.use(function(config) {
    var jwt = window.vm.$store.getters.getJwt;
    config.headers['jwt'] = jwt;
    return config;
}, function(error) {
    return Promise.reject(error);
});

定义获取的方法:src/store/getters.js 

export default{
  getResturantName:(state)=>{
    return state.resturantName;
  },
  getJwt:(state)=>{
    return state.jwt;
  }
}

⑦总结

使用session的用户的信息是存储到服务器,没有储存到客户端,登录之后重新打开其他的页面也可以进行访问

而JWT是存储到客户端的,每次请求会得到一个令牌,即使登录过,打开其他页面也是不允许访问的,重新打开一个页面拿不到上一次请求的令牌,这就是JWT的安全性比session高

二、JWT工具类介绍,三种场景

JWT的数据结构

 

        Jwt头:头部信息,一个字符串

        有效载荷:用户信息

        签名:密码本,密钥,加密的东西

JwtUtils.java

package com.zking.vue.util;
 
import java.util.Date;
import java.util.Map;
import java.util.UUID;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.binary.Base64;
 
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
 
/**
 * JWT验证过滤器:配置顺序 CorsFilte->JwtUtilsr-->StrutsPrepareAndExecuteFilter
 *
 */
public class JwtUtils {
    /**
     * JWT_WEB_TTL:WEBAPP应用中token的有效时间,默认30分钟
     */
    public static final long JWT_WEB_TTL = 30 * 60 * 1000;
 
    /**
     * 将jwt令牌保存到header中的key
     */
    public static final String JWT_HEADER_KEY = "jwt";
 
    // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;
    private static final String JWT_SECRET = "f356cdce935c42328ad2001d7e9552a3";// JWT密匙
    private static final SecretKey JWT_KEY;// 使用JWT密匙生成的加密key
 
    static {
        byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);
        JWT_KEY = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
    }
 
    private JwtUtils() {
    }
 
    /**
     * 解密jwt,获得所有声明(包括标准和私有声明)
     * 
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJwt(String jwt) {
        Claims claims = Jwts.parser().setSigningKey(JWT_KEY).parseClaimsJws(jwt).getBody();
        return claims;
    }
 
    /**
     * 创建JWT令牌,签发时间为当前时间
     * 
     * @param claims
     *            创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
     * @param ttlMillis
     *            JWT的有效时间(单位毫秒),当前时间+有效时间=过期时间
     * @return jwt令牌
     */
    public static String createJwt(Map<String, Object> claims, long ttlMillis) {
        // 生成JWT的时间,即签发时间
        long nowMillis = System.currentTimeMillis();
 
        // 下面就是在为payload添加各种标准声明和私有声明了
        // 这里其实就是new一个JwtBuilder,设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                // 可以在未登陆前作为身份标识使用
                .setId(UUID.randomUUID().toString().replace("-", ""))
                // iss(Issuser)签发者,写死
                // .setIssuer("zking")
                // iat: jwt的签发时间
                .setIssuedAt(new Date(nowMillis))
                // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
                // .setSubject("{}")
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
                // 设置JWT的过期时间
                .setExpiration(new Date(nowMillis + ttlMillis));
 
        return builder.compact();
    }
 
    /**
     * 复制jwt,并重新设置签发时间(为当前时间)和失效时间
     * 
     * @param jwt
     *            被复制的jwt令牌
     * @param ttlMillis
     *            jwt的有效时间(单位毫秒),当前时间+有效时间=过期时间
     * @return
     */
    public static String copyJwt(String jwt, Long ttlMillis) {
        Claims claims = parseJwt(jwt);
 
        // 生成JWT的时间,即签发时间
        long nowMillis = System.currentTimeMillis();
 
        // 下面就是在为payload添加各种标准声明和私有声明了
        // 这里其实就是new一个JwtBuilder,设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                // 可以在未登陆前作为身份标识使用
                //.setId(UUID.randomUUID().toString().replace("-", ""))
                // iss(Issuser)签发者,写死
                // .setIssuer("zking")
                // iat: jwt的签发时间
                .setIssuedAt(new Date(nowMillis))
                // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可放数据{"uid":"zs"}。此处没放
                // .setSubject("{}")
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(SIGNATURE_ALGORITHM, JWT_KEY)
                // 设置JWT的过期时间
                .setExpiration(new Date(nowMillis + ttlMillis));
        return builder.compact();
    }
} 

三种场景

如何通过jwt的工具类去生成令牌以及解析令牌

生成JWT

解析JWT

令牌过期的场景

延续令牌的存活时间

package com.zking.vue.test;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
import org.junit.Test;
 
import com.zking.vue.util.JwtUtils;
 
import io.jsonwebtoken.Claims;
 
public class JwtDemo {
 
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 
    @Test
    public void test1() {// 生成JWT
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("username", "zss");
        claims.put("age", 18);
 
        String jwt = JwtUtils.createJwt(claims, JwtUtils.JWT_WEB_TTL);
        System.out.println(jwt);
 
        Claims parseJwt = JwtUtils.parseJwt(jwt);
        for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        Date d1 = parseJwt.getIssuedAt();
        Date d2 = parseJwt.getExpiration();
        System.out.println("令牌签发时间:" + sdf.format(d1));
        System.out.println("令牌过期时间:" + sdf.format(d2));
    }
 
    @Test
    public void test2() {// 解析oldJwt
//        String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
        String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzcxMzkxMDUsImlhdCI6MTYzNzEzNzMwNSwiYWdlIjoxOCwianRpIjoiYjRkYmNkYjNhMDhmNDFjZGFhMzk4NGU1YWU4NGRiYTkiLCJ1c2VybmFtZSI6InpzcyJ9.XuzZJo0BH_hJjV1-muAcL00uaRSLiCuNPs4f-0a8nfU";
        Claims parseJwt = JwtUtils.parseJwt(oldJwt);
        for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        Date d1 = parseJwt.getIssuedAt();
        Date d2 = parseJwt.getExpiration();
        System.out.println("令牌签发时间:" + sdf.format(d1));
        System.out.println("令牌过期时间:" + sdf.format(d2));
    }
 
    @Test
    public void test3() {// 复制jwt,并延时30秒
        String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI5MDMzNjAsImlhdCI6MTU2MjkwMTU2MCwiYWdlIjoxOCwianRpIjoiZDVjMzE4Njg0MDcyNDgyZDg1MDE5ODVmMDY3OGQ4NjkiLCJ1c2VybmFtZSI6InpzcyJ9.XDDDRRq5jYq5EdEBHtPm7GcuBz4S0VhDTS1amRCdf48";
        String jwt = JwtUtils.copyJwt(oldJwt, JwtUtils.JWT_WEB_TTL);
        Claims parseJwt = JwtUtils.parseJwt(jwt);
        for (Map.Entry<String, Object> entry : parseJwt.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
        Date d1 = parseJwt.getIssuedAt();
        Date d2 = parseJwt.getExpiration();
        System.out.println("令牌签发时间:" + sdf.format(d1));
        System.out.println("令牌过期时间:" + sdf.format(d2));
    }
 
    @Test
    public void test4() {// 测试JWT的有效时间
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("username", "zss");
        String jwt = JwtUtils.createJwt(claims, 3 * 1000L);
        System.out.println(jwt);
        Claims parseJwt = JwtUtils.parseJwt(jwt);
        Date d1 = parseJwt.getIssuedAt();
        Date d2 = parseJwt.getExpiration();
        System.out.println("令牌签发时间:" + sdf.format(d1));
        System.out.println("令牌过期时间:" + sdf.format(d2));
    }
 
    @Test
    public void test5() {// 三秒后再解析上面过期时间只有三秒的令牌,因为过期则会报错io.jsonwebtoken.ExpiredJwtException
        String oldJwt = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjI4NTMzMzAsImlhdCI6MTU2Mjg1MzMyNywidXNlcm5hbWUiOiJ6c3MifQ.e098Vj9KBlZfC12QSDhI5lUGRLbNwb27lrYYSL6JwrQ";
        Claims parseJwt = JwtUtils.parseJwt(oldJwt);
        // 过期后解析就报错了,下面代码根本不会执行
        Date d1 = parseJwt.getIssuedAt();
        Date d2 = parseJwt.getExpiration();
        System.out.println("令牌签发时间:" + sdf.format(d1));
        System.out.println("令牌过期时间:" + sdf.format(d2));
    }
}

到这里就结束了,欢迎大佬指点  

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JSON Web令牌(JWT)详解 的相关文章

随机推荐

  • java有abc三边求周长语句_java 给定三个点由三个点求三角形周长和面积

    3 先创建一个Point类 然后定义Trianglele类 在Trianglele类中定义三个Point的实体来表示一个三角形的三个点 再定义一个方法setTri对这三个点进行初始化 然后定义两个方法求三角形 3 先创建一个Point类 然
  • (个人)自媒体快传号注册入驻流程

    快传号是360推出的自媒体平台 支持手机号注册 简单便捷 目前支持入驻的账号类型有 个人 媒体 企业 政府和其他组织 个人 适合个人创作者 垂直领域专家 意见领袖及自媒体人士申请 媒体 适合通讯社 报刊杂志 电视台 电台等传统媒体及有新闻资
  • Python智力问答小游戏

    Python智力问答小游戏 在这个Python智力问答小游戏中 我们将提供一系列问题 并编写代码来实现一个简单的问答游戏 玩家将被要求回答一些与Python编程相关的问题 并根据他们的回答来获得得分 让我们一起来创建这个有趣的游戏吧 首先
  • python: Call SQL Server Stored Procedure in Python

    sql script DROP TABLE InsuranceMoney GO create table InsuranceMoney ID INT IDENTITY 1 1 PRIMARY KEY InsuranceName nvarch
  • PCB中常用快捷键

    网络搜集 备份一份方便查看 PCB快速挖孔 T V B 绘制闭合区域后 直接选中所有线条 然后T V B 挖孔 AD布线 选定器件 框定到指定区域的快捷键 原理图框定指定模块后 按T S后跳到PCB自动回选中相关器件 然后按T O L划定一
  • 什么是面向对象编程?

    所有编程语言都提供抽象机制 汇编是语言是对底层机器的轻微抽象 命令式语言 FORTRAN BASIC C 是对底层机器的轻微抽象 他们所做的主要抽象仍然要求在解决问题基于计算机的结构 而不是所要解决问题的结构 程序员必须建立起机器模型和待解
  • 第三周 Python基本数据类型

    1 数字类型及操作 1 1整数类型 与数学中整数的概念一致 可正可负 没有取值范围限制 pow x y 函数 计算xy 想算多大算多大 gt gt gt 1267650600228229401496703205376 4种进制表示形式 十进
  • npm ERR network Invalid response body while trying to fetch

    问题描述 安装 vue cli 的时候持续报错 npm notice npm notice New minor version of npm available 8 5 0 gt 8 7 0 npm notice Changelog htt
  • 出现undefined reference to `forkpty' 错误解决方法

    出现undefined reference to forkpty 错误解决方法 2009 02 04 09 14 23 分类 C 举报 字号 订阅 下载LOFTER客户端 出错提示如下 usr lib lib libpython2 5 so
  • C语言是一种非结构化的程序设计语言,C语言程序设计——姜恒远 第一章 C程序设计概述.ppt...

    C语言程序设计 姜恒远 第一章 C程序设计概述 姜 恒 远 第1章 C程序设计概述 1 1 程序设计语言 程序与程序设计 1 1 1 程序设计语言 CPU能理解且能直接执行的指令集合 用机器语言编写的程序形式 用机器语言编写的程序优点 质量
  • 三、mock与umi-plugin-react插件

    一 Mock 数据 umi 里约定 mock 文件夹下的文件或者 page s 文件夹下的 mock 文件即 mock 文件 这个mock数据 默认导出的结果 前面为请求方式和请求的url 后面的为返回前端的结果 结果可以是函数 也可以是对
  • 在centos7上部署ZeroTier实现内网穿透

    https my zerotier com 登录账户后在ttps my zerotier com network创建网络 配置centos yum源 vi etc yum repos d zerotier repo zerotier nam
  • c++tuple和bitset

    tuple tuple是类似pair的模板 一个tuple可以有任意数量的成员 类型也可以不相同 make tuple 返回一个用给定初始值初始化的tuple 返回的tuple类型从初始值推断 可以使用 初始化但不能使用 tuple
  • Visual Studo Code & Anaconda环境配置

    在使用VScode的过程中 遇到如下问题 通过Anaconda安装的库文件在VScode中无法import 提示找不到该module 但在window的cmd中是可以import该库文件的 原因 环境配置问题 1 选择了错误的python解
  • 谈谈对于XSS跨站脚本攻击的学习(1)

    前言 最近学完XXE之后 对于这种恶意代码注入的漏洞提起来兴趣 想着现在正好趁热打铁 学习一下XSS 之前做题的时候看大师傅的wp一愣一愣的 不明白个所以然 这次系统的学习一下 在本文中将介绍有关XSS的知识点以及原理 也会介绍XSS的绕过
  • web前端期末大作业实例 (1500套) 集合

    文章目录 web前端期末大作业 1500套 集合 一 网页介绍 二 网页集合 表白网页 125套 集合 Echarts大屏数据展示 150套 集合 一 基于HTML Echarts技术制作 二 基于VUE Echarts技术制作 更多源码
  • 微信小程序腾讯位置服务添加不上去

    今天发现怎么添加都添加不上去 然后我搜索了大半天 emm最后都没有找到我的解决方法 然后终于我想起来他的报错提示 去搜了一下类目 然后去小程序添加了个交通类目 然后就好了
  • SOLO训练代码解析

    之前写过对SOLO demo的代码解析 今天来梳理一下training过程 首先是tools train py 这个文件是训练的开始 命令行运行的就是该文件 from mmdet apis import set random seed tr
  • Python使用国内镜像安装

    命令 pip install i 国内镜像地址 numpy 国内常用源镜像地址 清华 https pypi tuna tsinghua edu cn simple 阿里云 http mirrors aliyun com pypi simpl
  • JSON Web令牌(JWT)详解

    前言 今天要分享的知识是JWT 码字不易 转载请说明 目录 一 JWT出现的原因及工作原理 JWT是什么 为什么使用JWT JWT的工作原理 JWT组成 传统开发对资源的访问限制利用session完成图解 JWT所解决的问题及机制 JWT解