通过appId和appSecret生成accessToken访问api后端接口(接口授权)

article/2025/9/12 6:06:05
  1. 功能点

(1)申请获取appId和appSecret

(2)通过appId和appSecret获取accessToken

(3)mysql和redis进行key-value键值对存储

(4)对需要的接口url进行请求地址拦截

效果图:

(1)正确的accessToken且没有过期(获取后端数据)

(2)没有填入的accessToken(提示为空)

(3)accessToken正确但是已经过了TTL时间(过期),redis进行数据删除。

(4)默认是7200s

具体实现:

(1) 数据库

/*Navicat Premium Data TransferSource Server         : 本机Source Server Type    : MySQLSource Server Version : 50738 (5.7.38-log)Source Host           : localhost:3306Source Schema         : couponTarget Server Type    : MySQLTarget Server Version : 50738 (5.7.38-log)File Encoding         : 65001Date: 20/03/2023 10:55:04
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for m_app
-- ----------------------------
DROP TABLE IF EXISTS `m_app`;
CREATE TABLE `m_app`  (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',`app_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '所申请应用名称',`app_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'appId',`app_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '秘钥',`is_flag` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '是否可用代表状态',`access_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '令牌',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of m_app
-- ----------------------------
INSERT INTO `m_app` VALUES (20, '测试', '4xMTxI9p', 'd1124a2acbc0074900e436505ccfa049f9abaa2f', '0', '95818324c9e34ff89d07f99772dee10c');
INSERT INTO `m_app` VALUES (21, '积分优惠券测试', 'W0wrjQ6C', '1bf37c433b627f9d2451d250916c866ee341de86', '0', '24e2fa591d9447c9b2745d84cda3c8da');
INSERT INTO `m_app` VALUES (22, 'test', 'WS09OO3s', 'cfe6b77fb68f213dedeb59f51b1a4808bfdec394', '0', 'a48e4e98314d4fa390ab31d49dd7757e');
INSERT INTO `m_app` VALUES (23, '123', 'tw9PDmTA', 'd35dd6ad355d6d85e8e9bb1682ea99952ca14d85', '0', '0c4734a413b54c86b8864e3f7aa87d60');
INSERT INTO `m_app` VALUES (24, '12345', 'OchZx9yE', '18e5892a2b92c016078eac4d41b25c765588073477c25b42e2c6b75859960e59', '0', '11c624f4e5394f948542673c393a820b');
INSERT INTO `m_app` VALUES (25, '1', 'qiKL6XKK', '9676fd0a49381cbdb3e86ad6547e58328594e349', '0', '7f15452ea17e4b9b9bddb3750952f787');
INSERT INTO `m_app` VALUES (26, '2121', 'hrGqnHC7', '4b684d90573d4bbeb4e2c504b219b3429eacc4d4', '0', '5682c190b8b54fd4904c8bf007e8708e');
INSERT INTO `m_app` VALUES (27, 'ghd', 'O5ELmevg', '0b45dead09e8195053eaeb8b7451a953572bfed5', '0', '3e80b55f73a54a3895be87fe3d334eb3');SET FOREIGN_KEY_CHECKS = 1;

(2)实体类:

package com.coupon_test.coupon.model;import com.baomidou.mybatisplus.annotation.TableName;@TableName("m_app")
public class AppEntity {private long id;private String appId;private String appName;private String appSecret;private String accessToken;private int isFlag;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getAppId() {return appId;}public void setAppId(String appId) {this.appId = appId;}public String getAppName() {return appName;}public void setAppName(String appName) {this.appName = appName;}public String getAppSecret() {return appSecret;}public void setAppSecret(String appSecret) {this.appSecret = appSecret;}public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getIsFlag() {return isFlag;}public void setIsFlag(int isFlag) {this.isFlag = isFlag;}@Overridepublic String toString() {return "AppEntity [id=" + id + ", appId=" + appId + ", appName=" + appName + ", appSecret=" + appSecret+ ", accessToken=" + accessToken + ", isFlag=" + isFlag + "]";}}

(3)mapper层:

package com.coupon_test.coupon.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.coupon_test.coupon.model.AppEntity;import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;public interface AppMapper extends BaseMapper<AppEntity> {@Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag , access_token as accessToken from m_app "+ "where app_id=#{appId} and app_secret=#{appSecret}  ")AppEntity findApp(AppEntity appEntity);@Select("SELECT ID AS ID ,APP_NAME AS appName, app_id as appId, app_secret as appSecret ,is_flag as isFlag  access_token as accessToken from m_app "+ "where app_id=#{appId} and app_secret=#{appSecret}  ")AppEntity findAppId(@Param("appId") String appId);@Update(" update m_app set access_token =#{accessToken} where app_id=#{appId} ")int updateAccessToken(@Param("accessToken") String accessToken, @Param("appId") String appId);}

(4)工具类,main方法可以进行测试。

package com.coupon_test.coupon.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;/*** @Title: AppUtils* @Description: 随机产生唯一的app_key和app_secret* @date 2023-02-15*/
public class AppUtils {//生成 app_secret 密钥private final static String SERVER_NAME = "积分优惠券系统";private final static String[] chars = new String[]{"a", "b", "c", "d", "e", "f","g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s","t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I","J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V","W", "X", "Y", "Z"};/*** @Description: <p>* 短8位UUID思想其实借鉴微博短域名的生成方式,但是其重复概率过高,而且每次生成4个,需要随即选取一个。* 本算法利用62个可打印字符,通过随机生成32位UUID,由于UUID都为十六进制,所以将UUID分成8组,每4个为一组,然后通过模62操作,结果作为索引取出字符,* 这样重复率大大降低。* 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。* </p>* @date 2023-02-15*/public static String getAppId() {StringBuffer shortBuffer = new StringBuffer();String uuid = UUID.randomUUID().toString().replace("-", "");for (int i = 0; i < 8; i++) {String str = uuid.substring(i * 4, i * 4 + 4);int x = Integer.parseInt(str, 16);shortBuffer.append(chars[x % 0x3E]);}return shortBuffer.toString();}/*** <p>* 通过appId和内置关键词生成APP Secret* </P>** @date 2023-02-15*/public static String getAppSecret(String appId) {try {String[] array = new String[]{appId, SERVER_NAME};StringBuffer sb = new StringBuffer();// 字符串排序Arrays.sort(array);for (int i = 0; i < array.length; i++) {sb.append(array[i]);}String str = sb.toString();MessageDigest md = MessageDigest.getInstance("SHA-256");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();throw new RuntimeException();}}public static void main(String[] args) {String appId = getAppId();System.out.println(appId);System.out.println(getAppSecret(appId));}
}

(5)请求拦截

package com.coupon_test.coupon.config;import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import com.coupon_test.coupon.service.BaseApiService;
import com.coupon_test.coupon.service.BaseRedisService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSONObject;//验证AccessToken 是否正确
@Component
public class AccessTokenInterceptor extends BaseApiService implements HandlerInterceptor {@Autowiredprivate BaseRedisService baseRedisService;/*** 进入controller层之前拦截请求** @param httpServletRequest* @param httpServletResponse* @param o* @return* @throws Exception*/public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)throws Exception {System.out.println("---------------------开始进入请求地址拦截----------------------------");String accessToken = httpServletRequest.getParameter("accessToken");// 判断accessToken是否空if (StringUtils.isEmpty(accessToken)) {// 参数Token accessTokenresultError(" this is parameter accessToken null ", httpServletResponse);return false;}String appId = (String) baseRedisService.getString(accessToken);if (StringUtils.isEmpty(appId)) {// accessToken 已经失效!resultError(" this is  accessToken Invalid ", httpServletResponse);return false;}// 正常执行业务逻辑...return true;}public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,ModelAndView modelAndView) throws Exception {System.out.println("--------------处理请求完成后视图渲染之前的处理操作---------------");}public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object o, Exception e) throws Exception {System.out.println("---------------视图渲染之后的操作-------------------------");}// 返回错误提示public void resultError(String errorMsg, HttpServletResponse httpServletResponse) throws IOException {PrintWriter printWriter = httpServletResponse.getWriter();printWriter.write(new JSONObject().toJSONString(setResultError(errorMsg)));}}

(6)配置类

package com.coupon_test.coupon.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebAppConfig {@Autowiredprivate AccessTokenInterceptor accessTokenInterceptor;@Beanpublic WebMvcConfigurer WebMvcConfigurer() {return new WebMvcConfigurer() {public void addInterceptors(InterceptorRegistry registry) {//  /openApi   下的所有接⼝registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*");registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*");registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*");registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*/*");registry.addInterceptor(accessTokenInterceptor).addPathPatterns("/api/*/*/*/*/*");};};}
}
package com.coupon_test.coupon.service;
import com.coupon_test.coupon.common.constants.Constants;
import com.coupon_test.coupon.model.system.ResponseBase;
import org.springframework.stereotype.Component;@Component
public class BaseApiService {public ResponseBase setResultError(Integer code, String msg) {return setResult(code, msg, null);}// 返回错误,可以传msgpublic ResponseBase setResultError(String msg) {return setResult(Constants.HTTP_RES_CODE_500, msg, null);}// 返回成功,可以传data值public ResponseBase setResultSuccessData(Object data) {return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data);}public ResponseBase setResultSuccessData(Integer code, Object data) {return setResult(code, Constants.HTTP_RES_CODE_200_VALUE, data);}// 返回成功,沒有data值public ResponseBase setResultSuccess() {return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null);}// 返回成功,沒有data值public ResponseBase setResultSuccess(String msg) {return setResult(Constants.HTTP_RES_CODE_200, msg, null);}// 通用封装public ResponseBase setResult(Integer code, String msg, Object data) {return new ResponseBase(code, msg, data);}}
package com.coupon_test.coupon.service;import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;@Component
public class BaseRedisService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** TODO    添加/更新* @param key  键* @param data  值* @param timeout 时间(秒)* @return void*/public void setString(String key, Object data, Long timeout) {if (data instanceof String) {String value = (String) data;stringRedisTemplate.opsForValue().set(key, value);}if (timeout != null) {//重新设置过期时间,刷新时间stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);}}/*** TODO    读取* @param key 键* @return java.lang.Object*/public Object getString(String key) {return stringRedisTemplate.opsForValue().get(key);}/*** TODO    删除* @param key 键* @return void*/public void delKey(String key) {stringRedisTemplate.delete(key);}
}

(7)controller层

package com.coupon_test.coupon.controller;import com.coupon_test.coupon.dao.AppMapper;
import com.coupon_test.coupon.model.AppEntity;
import com.coupon_test.coupon.model.system.ResponseBase;
import com.coupon_test.coupon.service.BaseApiService;
import com.coupon_test.coupon.service.BaseRedisService;
import com.coupon_test.coupon.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;import java.util.*;// 创建获取getAccessToken
@RestController
@RequestMapping(value = "/auth")
public class AuthController extends BaseApiService {@Autowiredprivate BaseRedisService baseRedisService;private long timeToken = 60 * 60 * 2;@Autowiredprivate AppMapper appMapper;// 使用appId+appSecret 生成AccessToke@RequestMapping(value = "/getAccessToken", method = RequestMethod.GET)public ResponseBase getAccessToken(AppEntity appEntity) {AppEntity appResult = appMapper.findApp(appEntity);if (appResult == null) {return setResultError("没有对应机构的认证信息");}int isFlag = appResult.getIsFlag();if (isFlag == 1) {return setResultError("您现在没有权限生成对应的AccessToken");}// ### 获取新的accessToken 之前删除之前老的accessToken// 从redis中删除之前的accessTokenString accessToken = appResult.getAccessToken();baseRedisService.delKey(accessToken);// 生成的新的accessTokenString newAccessToken = newAccessToken(appResult.getAppId());JSONObject jsonObject = new JSONObject();jsonObject.put("accessToken", newAccessToken);return setResultSuccessData(jsonObject);}// 使用appId+appSecret 生成AccessToke@RequestMapping(value = "/insertApp", method = RequestMethod.POST)public ResponseBase save(AppEntity appEntity) {String appId = AppUtils.getAppId();String appSecret = AppUtils.getAppSecret(appId);AppEntity appEntity1 = new AppEntity();appEntity1.setIsFlag(0);appEntity1.setAppName(appEntity.getAppName());appEntity1.setAppId(appId);appEntity1.setAppSecret(appSecret);appEntity1.setAccessToken("");int insert = appMapper.insert(appEntity1);if (insert == 1) {return setResultSuccessData("appId是" + appId + "-----" + "appSecret是" + appSecret);} else {return setResultError("添加失败");}}private String newAccessToken(String appId) {// 使用appid+appsecret 生成对应的AccessToken 保存两个小时
//        String accessToken = TokenUtils.getAccessToken();String accessToken = UUID.randomUUID().toString().replace("-", "");// 保证在同一个事物redis 事物中// 生成最新的token key为accessToken value 为 appidbaseRedisService.setString(accessToken, appId, timeToken);// 表中保存当前accessTokenappMapper.updateAccessToken(accessToken, appId);return accessToken;}}

(8)依赖

   <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency>
   <!--字符串操作--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.2.1</version></dependency>

先使用controller获取appId及appSecret, 接着申请accessToken并带至请求中,获取返回数据。

(二)、将请求的http协议改为https

效果图:postman测试

  1. 首先必须的依赖是

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 使用命令获取SSL文件

keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
  1. application.yml文件配置

server:port: 8081ssl:enabled: truekey-store: classpath:keystore.p12key-store-type: PKCS12key-alias: tomcatkey-store-password: caojun

注意是key-store-password


http://chatgpt.dhexx.cn/article/cQapFPv4.shtml

相关文章

微信开发者之AppID和AppSecret举例子

-- 请不要为爱你的人流泪&#xff0c;因为爱你的人不会让你流泪&#xff01; 序 今天对这2东西做个相对全面的介绍&#xff0c;不一定深入&#xff0c;但是对实际开发还是很有必要的 我们在微信开发中总是会绕不开2个单词&#xff1a; AppID&#xff1a;开发者ID,有些叫AppK…

安全课堂|关于小程序AppSecret密钥泄露漏洞官方

原文链接:https://developers.weixin.qq.com/community/minihome/doc/0004a84fcb0bb0e89eddbaa5156401 安全课堂&#xff5c;关于小程序AppSecret密钥泄露漏洞官方 微信团队04-27 为进一步提升小程序的安全性和用户体验&#xff0c;目前平台对提审的小程序均需进行安全检测&…

【微信小程序】项目上线所需AppID、AppSecret和原始ID获取的逐步方法

一、简述 本文内容面向业务老师&#xff0c;通过详细的逐步方法&#xff0c;帮助对流程不熟悉的有微信小程序管理员权限的业务老师&#xff0c;通过微信公众平台&#xff0c;获得项目上线所需的AppID、AppSecret、原始ID&#xff0c;不熟悉流程的开发人员也可以阅读。 二、进…

微信小程序AppID、AppSecret如何获取

进入微信公众平台 https://mp.weixin.qq.com/ 获取AppID&#xff08;小程序ID&#xff09; 微信公众平台 - 开发 - 开发管理 - 开发设置 获取AppSecret 管理员用户&#xff0c;同AppID页面&#xff0c;点击“生成”&#xff0c;生成小程序秘钥 注意&#xff1a;尽量只生成一次&…

值得收藏!这 100 个 Python 实战小项目真棒!

学习 Python 会有这么一个阶段&#xff0c;太简单的程序看不上眼&#xff0c;复杂的开源项目又有点力不从心&#xff0c;这个时候&#xff0c;你就需要接触点简单的 Python 小项目来提升 Python 技能。 最近逛GitHub, 碰巧碰上有这样一个项目&#xff0c;收集了 100 个实用的 …

2022最新python100个实战练手项目,【附源码】,快来学习起来吧!

Python是目前最好的编程语言之一。由于其可读性和对初学者的友好性&#xff0c;已被广泛使用。那么要想学会并掌握Python&#xff0c;可以实战的练习项目是必不可少的。 接下来&#xff0c;我将给大家介绍20个非常实用的Python项目&#xff0c;帮助大家更好的学习Python。大家…

python爬虫实战小项目

本文所讲的爬虫实战属于基础、入门级别&#xff0c;使用的是python2.7实现的。 爬虫原理和思想 本项目实现的基本目标&#xff1a;在捧腹网中&#xff0c;把搞笑的图片都爬下来&#xff0c;注意不需要爬取头像的图片&#xff0c;同时&#xff0c;将图片命好名放在当前的img文件…

django项目实战基于Python实现的电影推荐系统

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Python项目介绍 资料获取 Python项目介绍 计算机毕业设计python毕设项目之django电影推荐系统-IT实战课堂_哔哩哔哩_bilibili计算机毕业设计python毕设项目之django电影推荐系统-IT实战…

给缺少Python项目实战经验的人

我们在学习过程中最容易犯的一个错误就是&#xff1a;看的多动手的少&#xff0c;特别是对于一些项目的开发学习就更少了&#xff01; 没有一个完整的项目开发过程&#xff0c;是不会对整个开发流程以及理论知识有牢固的认知的&#xff0c;对于怎样将所学的理论知识应用到实际…

Python项目实战 —— 淘宝用户行为分析

Python项目实战 Python项目实战 —— 0淘宝用户行为分析 一、背景 本数据集共有大约1200万条数据&#xff0c;数据为淘宝APP2014年11月18日至2014年12月18日的用户行为数据&#xff0c;字段分别是&#xff1a;user_id用户身份(已脱敏)、item_id商品ID(已脱敏)、behavior_type…

Python实战小项目

不是很稀饭《复联》嘛&#xff0c;看了《复联4》&#xff0c;就用50行Python代码做了这些&#xff1a; 视频展示&#xff1a;50行代码玩转《复仇者联盟》 教程地址&#xff1a;图片转字符画 相关教程地址&#xff1a;视频转字符动画 当然&#xff0c;这个仅仅是初学者的一个…

手把手教导实战Python Web项目

手把手教导实战Python Web项目 一、前言 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MVC的框架模式&#xff0c;即模型M&#xff0c;视图V和控制器C。Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用&#xff0c;多个组件可以很…

Python web开发之flask项目实战总结

在线问答系统 Flask Web开发项目介绍功能描述一、前端设计二、数据库设计三、页面功能开发1.用户模块2.问答模块 四、总结 Flask Web开发 请求到响应 用户通过浏览器访问url&#xff0c;会发送请求到服务器&#xff0c;服务器接收请求后会根据url规则找到对应的视图函数进…

Python必学的4个实战项目,拿走不谢

最近遇到了一些比较经典的实战项目&#xff0c;比较适合零基础的小白以及基础较薄弱的小伙伴。想了想&#xff0c;我决定把它分享给大家。 Python是一种极具可读性和通用性的编程语言。Python这个名字的灵感来自于英国喜剧团体Monty Python&#xff0c;它的开发团队有一个重要的…

菜鸟进阶高手, 推荐 7 个 Python 上手实战项目

作为一个语法简洁、有着丰富的第三方库的编程语言&#xff0c;Python 上手极为简单&#xff0c;短时间内就可以让你编写出能够解决实际问题的小程序&#xff0c;甚至去面试初级 Python 工程师的职位。 不过&#xff0c;如果要写出一些更复杂的应用&#xff0c;或者想从事数据分…

比金典还经典——4个python项目实战

写在前面的一些P话&#xff1a; Python是一种极具可读性和通用性的编程语言。Python这个名字的灵感来自于英国喜剧团体Monty Python&#xff0c;它的开发团队有一个重要的基础目标&#xff0c;就是使语言使用起来很有趣。Python易于设置&#xff0c;并且是用相对直接的风格来编…

Python项目实战 4.1:账号登录

目录 一、用户名登录 二、多账号登录 三、首页用户名展示 四、退出登录 五、判断用户是否登录 一、用户名登录 1. 用户名登录逻辑分析 2. 用户名登录接口设计 1. 请求方式 选项方案请求方法POST请求地址/login/ 2. 请求参数&#xff1a;表单 参数名类型是否必传说明usern…

Python爬虫实战

文章目录 1. 引言2. 页面分析2.1 页面元素分析2.2 分页分析2.3 页面详情页面2.4 下载链接 3. 代码3.1 数据库结构3.2 步骤3.2.1 根据url获取页面结构3.2.2 解析页面数据3.2.3 数据存入数据库 4. 测试结果5. 完整代码 1. 引言 注&#xff1a;勿用于非法用途。 之前学习过Pytho…

insert into

7.insert into 如果我们想向表格中插入数据&#xff0c;就需要用到insert into语句了 7.1 insert into语法 INSERT INTO 表名 VALUES (值1, 值2,....) &#xff08;注&#xff1a;插入一行数据&#xff09; INSERT INTO 表名 (列名, 列名…

LaTeX: Missing } or { inserted. ^^I\For

报错 分析 可以发现&#xff0c;报错提示缺少{&#xff0c;但第306行并没有问题&#xff0c;并不缺少大括号。 这种情况下&#xff0c;很有可能是前面某个公式处出现了大括号缺失&#xff0c;建议从头到尾查找一遍。 修改 往前排查后发现某一处缺少大括号&#xff0c;如下所…