vue+springboot实现JWT登录验证

目录

  • 前言
    • 概念
    • 实际演示
        • 路由信息
        • 初始访问登录界面
        • 登录验证
        • 验证过期
    • vue实现
      • 依赖引入
      • main.js
      • 获取和设置token工具类
      • 登录方法
        • 实体
        • 登录方法
        • axios请求
      • router配置
    • springboot实现
      • 依赖引入
      • JWT工具类
      • 忽视jwt验证注解
      • 拦截器逻辑
      • 跨域&调用拦截器配置
      • 登录接口&验证token接口
  • 结语

前言

最近在研究SSO(单点登录)系统,对于内部是如何实现登录验证的产生了兴趣,而现在终于研究出它是如何验证成功的,接下来我将讲解如何通过vue和springboot实现Jwt验证登录
🌺🌹🥀🌺🥀🌹🌺🌹🥀🌺🥀🌹

概念

在正式开始之前,我同样会讲解一下概念
单点登录:

单点登录(Single Sign-On, SSO)是一种身份认证授权机制,允许用户在多个应用程序或系统中进行登录,并在登录后可以无需重新输入凭据就能访问其他应用程序。通过SSO,用户只需登录一次,即可获得对多个相关但独立的软件系统或资源的访问权限。

那么这篇文章,只会讲解如何实现身份认证,并不会讲解如何实现SSO

🟠🟡🔴🟠🟣🔵🟡🟠🟣


JWT:

JWT全称为JSON Web Token,是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。它通常用于在用户和服务之间传递身份认证信息和声明。JWT通常被用作实现身份验证和授权的标准方法。

JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token,并且这个JWT token带有签名信息,接收后可以校验是否被篡改

🌺🌹🥀🌺🥀🌹🌺🌹🥀🌺🥀🌹

实际演示

路由信息

在我的项目中,我的router页面有这些:

export const constantRoutes = [{path: '/login',component: () => import('@/views/login/index'),hidden: true},{path: '/main',component: () => import('@/views/main'),children: [{path: '',name: 'dashBorad',component: () => import('@/views/dashBorad')},{path: '/menuPage',name: 'menuPage',component: () => import('@/views/menuPage')},{path: '/userDataManage',name: 'userDataManage',component: () => import('@/views/user/userDataManage')}]},{path: '/404',component: () => import('@/views/404'),hidden: true},// 404 page must be placed at the end !!!{path: '*', redirect: '/404', hidden: true}
]
初始访问登录界面

初始我访问界面,会到登录界面
在这里插入图片描述
假如我在未登录的情况下想访问其他路由,会禁止:
在这里插入图片描述
自动会跳转到登录界面
在这里插入图片描述

登录验证

假如输入账号密码登录之后,才能进入到系统内:
在这里插入图片描述
这个时候能够切换到不同的界面:
在这里插入图片描述
并且能够调用后端接口查询数据:
在这里插入图片描述

验证过期

但是一旦jwt验证过期,为演示方便,这边将手动把token修改错误

此时再跳转界面和查数据都会,报错,且跳转到登录页:
在这里插入图片描述
在这里插入图片描述

🧡💚💛🧡💜🧡🧡💚💛🧡💜🧡
🌺🌷🌻🌼🌷🌺🌷🌻🌼🌷🌻🌼~~~~~~~~
🧡💚💛🧡💜🧡🧡💚💛🧡💜🧡

vue实现

🌴🌳🍀🌲🥀🍁

依赖引入

在我的项目中,涉及相关JWT验证的有如下:

// axios
npm i axios@1.5.0
// elementui
npm i element-ui -S
// router
npm i vue-router
// js-cookie
npm i js-cookie

🧡🧡🧡🧡🧡🧡🧡🧡🧡🧡🧡🧡

main.js

这时main.js的代码如下:

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = falseVue.use(ElementUI)
new Vue({el: '#app',router,components: { App },template: '<App/>'
})

💛💛💛💛💛💛💛💛💛💛💛💛

获取和设置token工具类

这里写一个工具类专门获取和设置工具类
建一个token.js

import Cookies from 'js-cookie'
// 获取token的key,需要和后端一致
const TokenKey = 'Authorization'
// 获取token
export function getToken () {return Cookies.get(TokenKey)
}
// 设置token
export function setToken (token) {return Cookies.set(TokenKey, token)
}
// 移除token
export function removeToken () {return Cookies.remove(TokenKey)
}

💙💙💙💙💙💙💙💙💙💙💙💙

登录方法

因为我的登录方法存在别的逻辑,如验证码,记住我等等,因此,这里演示只给出最纯粹的登录逻辑

实体
// 设置用户名和密码登录loginForm: {username: 'admin',password: 'admin',},

登录方法涉及到引入

// 注意,这里文件的位置请根据自己实际项目文件位置进行修改
import { getToken, setToken } from '@/utils/token'
import {login} from '../../api/login'

🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸🌸

登录方法

点击按钮调用登录方法

    submitLogin () {if (!this.loginForm.username) {this.$message.error('用户名不能为空!')return}if (!this.loginForm.password) {this.$message.error('密码不能为空!')return}login(this.loginForm.username, this.loginForm.password).then(res => {if (res.header.code !== 0) {this.$message.error(res.header.message)return}// 设置tokensetToken(res.value.token)// 根据自己实际项目跳转主界面this.$router.push('/main')})},

当这里登录之后会在cookie位置新增数据(F12):
在这里插入图片描述
💐💐💐💐💐💐💐💐💐💐💐💐💐

axios请求

新增axios的工具类,进行封装,在这里会在调用之前确认是否验证过期
request.js: 新建js代码,代码如下:

import axios from 'axios'
import { Message, MessageBox, Notification } from 'element-ui'
import { getToken } from '@/utils/token'
import errorCode from '@/utils/errorCode'
import router from '../router/index'
import {removeToken} from './token'axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'// 创建axios实例
const service = axios.create({// 配置后端请求路径,根据自己实际项目修改baseURL: process.env.VUE_APP_BASE_API,// 请求超时时间 withCredentials: true,timeout: 30000
})// 请求统一拦截处理
service.interceptors.request.use(config => {// 是否需要设置 tokenif (getToken()) {config.headers['Authorization'] = getToken() // 请求均需携带自定义token}return config
},
error => {// 请求失败console.log(error) // for debug// return Promise.reject(error)return Promise.reject(error)
}
)// 响应拦截器
service.interceptors.response.use(res => {console.log('res.data', res.data)// 未设置状态码则默认成功状态const code = res.data.header.code || 200// 获取错误信息const msg = errorCode[code] || res.data.header.message || errorCode['default']if (code === 401) {return new Promise((resolve, reject) => {MessageBox.confirm('登录状态已过期,请重新登录', '系统提示', {confirmButtonText: '重新登录',showCancelButton: false,type: 'warning'}).then(() => {removeToken()router.push('/login')resolve() // 手动解决 Promise,避免重复导航})})} else if (code === 500) {Message({message: msg,type: 'error'})return Promise.reject(new Error(msg))} else if (code !== 0 && code !== 200) {Notification.error({title: msg})// eslint-disable-next-line prefer-promise-reject-errorsreturn Promise.reject('error')} else {return res.data}
}, error => {let { message } = errorif (message === 'Network Error') {message = '服务端连接异常'} else if (message.includes('timeout')) {message = '系统接口请求超时'} else if (message.includes('Request failed with status code')) {message = '系统接口' + message.substr(message.length - 3) + '异常'}Message({message: message,type: 'error',duration: 5 * 1000})return Promise.reject(error)
}
)export default service

上述会捕获后端返回的code,做不同的事情,是否可调用接口

针对上述的errorCode ,为新建的封装报错代码js,根据自己需要可做可不做
errorCode.js: 代码如下:

export default {'401': '认证失败,无法访问系统资源','403': '当前操作没有权限','404': '访问资源不存在','default': '系统未知错误,请反馈给管理员'
}

上述登录方法涉及到的axios的api如下
login.js

import request from '@/utils/request'// 登录方法
export function login (account, password) {const data = {account,password}return request({url: '/idle/login',method: 'post',data: data})
}// 验证token是否有效
export function verify (token) {let data = {token}return request({url: '/idle/verify',method: 'get',params: data})
}

router配置

跳转路由,拦截请求是否已经过期
新建router文件夹,在其中建立index.js
代码如下:

import Vue from 'vue'
import Router from 'vue-router'
// eslint-disable-next-line standard/object-curly-even-spacing
import {getToken, removeToken } from '@/utils/token'
import {verify} from '@/api/login'
import { Message } from 'element-ui'Vue.use(Router)export const constantRoutes = [{path: '/login',component: () => import('@/views/login/index'),hidden: true},{path: '/main',component: () => import('@/views/main'),children: [{path: '',name: 'dashBorad',component: () => import('@/views/dashBorad')},{path: '/menuPage',name: 'menuPage',component: () => import('@/views/menuPage')},{path: '/userDataManage',name: 'userDataManage',component: () => import('@/views/user/userDataManage')}]},{path: '/404',component: () => import('@/views/404'),hidden: true},// 404 page must be placed at the end !!!{path: '*', redirect: '/404', hidden: true}
]const createRouter = () => {const router = new Router({mode: 'hash',scrollBehavior: () => ({y: 0}),routes: constantRoutes})router.beforeEach((to, from, next) => {let token = getToken()if (!token) {// 如果未登录并且不是去往登录页,则跳转到登录页if (to.path !== '/login') {next('/login')} else {next() // 如果是去往登录页,则直接放行}} else {// 已登录状态verify(token).then(res => {let isVerify = res.value// 判断是否token验证成功,验证成功则跳转要去的路由,否则报错,跳回登录界面if (isVerify) {next()} else {removeToken()setTimeout(() => { next('/login') }, 1500)}}).catch(() => {next('/login') // 异步操作失败后再手动重定向})}})return router
}const router = createRouter()export function resetRouter () {const newRouter = createRouter()router.matcher = newRouter.matcher // reset router
}export default router

通过以上router.jsrequest.js,即可在跳转页面以及访问后端接口的时候进行拦截验证

🌼🌼🌼🌼🌻🌻🌻🌻🌻🌷🌷🌷🌷🌷🌷🌷🌼🌼🌼🌼🌻🌻🌻🌻🌻

springboot实现

依赖引入

同样,为了实现JWT,我们后端也需要做一些引入,注意:本次引入只涉及到JWT相关,其他自己项目相关请额外进行引入

    <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>

🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵

JWT工具类

新建工具类,命名JwtTokenUtil:
代码如下:

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import java.util.Date;public class JwtTokenUtil {//定义token返回头部public static final String AUTH_HEADER_KEY = "Authorization";//token前缀public static final String TOKEN_PREFIX = "Bearer ";//签名密钥public static final String KEY = "q3t6w9z$C&F)J@NcQfTjWnZr4u7x";//有效期默认为 2hourpublic static final Long EXPIRATION_TIME = 1000L * 60 * 60 * 2;/*** 创建TOKEN*/public static String createToken(String content) {return TOKEN_PREFIX + JWT.create().withSubject(content).withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).sign(Algorithm.HMAC512(KEY));}/*** 验证token*/public static String verifyToken(String token) throws Exception {try {return JWT.require(Algorithm.HMAC512(KEY)).build().verify(token.replace(TOKEN_PREFIX, "")).getSubject();} catch (TokenExpiredException e) {throw new Exception("token已失效,请重新登录", e);} catch (JWTVerificationException e) {throw new Exception("token验证失败!", e);}}public static Boolean verify(String token) throws Exception {try {JWT.require(Algorithm.HMAC512(KEY)).build().verify(token.replace(TOKEN_PREFIX, "")).getSubject();return true;} catch (Exception e) {return false;}}
}

忽视jwt验证注解

新建一个注解,用于忽视验证,比如登录,注册方法

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtIgnore {boolean value() default true;
}

🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱🌱

拦截器逻辑

import cn.hutool.json.JSONObject;
import com.pearl.Interface.JwtIgnore;
import com.pearl.utils.JwtTokenUtil;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {response.setCharacterEncoding("UTF-8");response.setContentType("application/json; charset=utf-8");// 从http请求头中取出tokenfinal String token = request.getHeader(JwtTokenUtil.AUTH_HEADER_KEY);//如果不是映射到方法,直接通过if (!(handler instanceof HandlerMethod)) {return true;}//如果方法有JwtIgnore注解,直接通过HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();if (method.isAnnotationPresent(JwtIgnore.class)) {JwtIgnore jwtIgnore = method.getAnnotation(JwtIgnore.class);if (jwtIgnore.value()) {return true;}}// 执行认证if (StringUtils.isEmpty(token)) {JSONObject res = new JSONObject();res.put("code", 401);res.put("msg", "无token,请重新登录");res.put("data", false);PrintWriter out = response.getWriter();out.append(res.toString());return false;}if (!JwtTokenUtil.verify(token)) {JSONObject res = new JSONObject();res.put("code", 401);res.put("msg", "token验证失败,请重新登录");res.put("data", false);PrintWriter out = response.getWriter();out.append(res.toString());return false;}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {}
}

🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳🌳

跨域&调用拦截器配置

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class GlobalWebMvcConfig implements WebMvcConfigurer {/*** 重写父类提供的跨域请求处理的接口*/@Overridepublic void addCorsMappings(CorsRegistry registry) {// 添加映射路径registry.addMapping("/**").allowedOriginPatterns("*")  // 允许所有来源.allowCredentials(true)      // 允许发送身份验证凭据.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS", "HEAD").allowedHeaders("*").exposedHeaders("Server", "Content-Length", "Authorization", "Access-Token","Access-Control-Allow-Origin", "Access-Control-Allow-Credentials");}// 添加拦截器,我的项目的基础路径为sso,登录接口路径为/sso/idle/login// addPathPatterns是拦截所有路径,excludePathPatterns是排除需要拦截的路径@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/**").excludePathPatterns("/sso/idle/login");}
}

🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴🌴

登录接口&验证token接口


import com.pearl.Interface.JwtIgnore;
import com.pearl.entitys.beans.UserLoginData;
import com.pearl.entitys.dataBaseTable.User;
import com.pearl.responseEntity.Response;
import com.pearl.service.LoginService;
import com.pearl.utils.db.PrimeDB;
import java.sql.Connection;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/idle")
public class LoginController {
// 我的数据库连接类@Autowiredprivate PrimeDB primeDB;
// service层,@Resourceprivate LoginService loginService;/*** 登录*/@JwtIgnore@PostMapping("/login")public Response<Map<String, Object>> login(@RequestBody UserLoginData userDto,HttpServletResponse response)throws Exception {try (Connection conn = primeDB.create()) {Map<String, Object> map = loginService.login(conn, userDto, response);return new Response<>(0, map, "登录成功");} catch (Exception e) {return new Response<>(1, e.getMessage());}}@JwtIgnore@GetMapping("/verify")public Response<Boolean> verify(@RequestParam("token") String token) {try {return new Response<>(0, loginService.verify(token), "验证成功!");} catch (Exception e) {return new Response<>(1, false, "验证失败");}}
}

🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾🌾
其中,验证接口的逻辑很简单,单纯调用JWT工具类进行判断即可,而登录方法根据不同的项目,可能各有区别,因此登录逻辑给出来只有参考意义.如下是loginService代码:

import com.alibaba.fastjson.JSONObject;
import com.pearl.db.UserDao;
import com.pearl.entitys.beans.UserLoginData;
import com.pearl.entitys.beans.UserToken;
import com.pearl.entitys.dataBaseTable.User;
import com.pearl.utils.AesUtil;
import com.pearl.utils.AssertUtils;
import com.pearl.utils.JwtTokenUtil;
import java.sql.Connection;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;@Service
public class LoginService {public Map<String, Object> login(Connection conn, UserLoginData userLoginData) throws Exception {try {Map<String, Object> map = new HashMap<>();/*** 校验账号* */UserDao userDao = new UserDao(conn);AssertUtils.notNull(userLoginData, "请求参数不能为空!");AssertUtils.isError(StringUtils.isEmpty(userLoginData.getAccount()), "账号不能为空!");AssertUtils.isError(StringUtils.isEmpty(userLoginData.getPassword()), "密码不能为空!");User user = userDao.selectbyUserId(userLoginData.getAccount());AssertUtils.notNull(user, "该账号不存在!");// 判断账号是否失效AssertUtils.isError(user.getStatus() != 1, "账号:" + user.getUserId() + "已失效!请联系管理员恢复!");// 验证账密Boolean isTruePass = new AuthService().checkPassword(userLoginData.getPassword(), user.getPassword(), user.getSalt());AssertUtils.isError(!isTruePass, "用户名或密码错误!");//TODO 获取用户权限UserToken userToken = new UserToken();BeanUtils.copyProperties(user, userToken);String token = JwtTokenUtil.createToken(JSONObject.toJSONString(userToken));map.put("user", user);map.put("token", token);return map;} catch (Exception e) {throw new Exception(e.getMessage());}}public Boolean verify(String token) throws Exception {try {return JwtTokenUtil.verify(token);} catch (Exception e) {throw new Exception(e.getMessage());}}
}

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
以上我有封装响应实体,我的响应实体代码如下:

public class Response<T> {public Header header;public T value;public Response() {}public Response(T value) {this.header = new Header();this.value = value;}public Response(int code, Exception ex) {if (ex.getMessage() == null) {this.header = new Header(code, ex.toString());} else {this.header = new Header(code, ex.getMessage());}this.value = null;}public Response(int code, String message) {this.header = new Header(code, message);this.value = null;}public Response(int code, T value, Exception ex) {if (ex.getMessage() == null) {this.header = new Header(code, ex.toString());} else {this.header = new Header(code, ex.getMessage());}this.value = value;}public Response(int code, T value, String message) {this.header = new Header(code, message);this.value = value;}// 请求头,包含响应码和响应提醒信息public static class Header {public int code;public String message;public Header() {this.code = 0;this.message = "";}public Header(int code, String message) {this.code = code;this.message = message;}}
}

如上我的调用登录数据结构如下:

{"header": {"code": 0,"message": "登录成功"},"value": {"user": {"userId": "admin","avatar": null,"userName": "超级管理员","password": "t3zluLHlyip9A8TcXrR05Q==","email": null,"phone": null,"sex": null,"age": 0,"status": 1,"createTime": "2024-04-07 08:11:43","updateTime": "2024-04-07 08:11:43"},"token": "Bearer xxx"}
}

因此前端可获取token数据,进行赋值设置
🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲🌲

结语

以上,为vue+springboot实现JWT登录验证过程

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

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

相关文章

【软考】下午题:面向对象与程序设计【接口、抽象类、this、super使用】

文章目录 1、位运算符2、特殊关键字的使用&#xff1a;break、continue3、成员变量&#xff08;类的属性&#xff09;和局部变量的区别4、this关键字5、super关键字6、抽象类与abstract关键字7、接口8、Java权限修饰符 1、位运算符 注意&#xff1a; ①右移根据最高位是0&#…

SOLIDWORKS如何新建定义材质库

SolidWorks材质库中包含了大量的材料选项&#xff0c;涵盖了金属、塑料、橡胶、复合材料等各种类型&#xff0c;每种材料都有详细的特性参数。用户可以根据设计需求&#xff0c;在材质库中选择合适的材料&#xff0c;从而更好地满足设计要求。在有限元分析中&#xff0c;需要附…

统一用安卓Studio修改项目包名

可以逃跑&#xff0c;可以哭泣&#xff0c;但不可以放弃 --《鬼灭之刃》 修改项目包名 1&#xff09;选中项目中药修改的包名&#xff1a; 2)目结构显示方式&#xff0c;取消 Compact Middle Packages 选项&#xff1b; 3)右键要修改的包名&#xff0c;选择 Refactor —— Re…

结构体及联合体大小计算

结构体大小计算 结构体大小的计算的依据是结构体内存对齐 对齐规则&#xff1a; 1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处 2.其他成员变量要对齐到某个数字&#xff08;对齐数&#xff09;的整数倍的地址处。 &#xff08;对齐数编译器默认的一个对齐…

超越常规:用PHP抓取招聘信息

在人力资源管理方面&#xff0c;有效的数据采集可以为公司提供宝贵的人才洞察。通过分析招聘网站上的职位信息&#xff0c;人力资源专员可以了解市场上的人才供给情况&#xff0c;以及不同行业和职位的竞争状况。这样的数据分析有助于企业制定更加精准的招聘策略&#xff0c;从…

redis的三大模式的演化及集群模式思考和总结

redis的三大模式&#xff0c;也是循序渐进。 1、主从复制 比如一开始的读写分离的&#xff0c;主从复制。 一个master&#xff0c;多个slave。 master进行写和 增量同步&#xff0c;slave负责读&#xff0c;和接收增量同步的信息。 这样压力减轻。 2、哨兵模式 这个推出…

打印CSDN博客只需两步

打印博客 关闭浏览器限制 浏览器打开对应博客&#xff0c;F12&#xff0c;在console下粘贴如下代码&#xff0c;回车 (function doPrint(){var head_str "<html><head><title></title></head><body>"; var foot_str "&…

4月9日学习记录

[GXYCTF 2019]禁止套娃 涉及知识点&#xff1a;git泄露&#xff0c;无参数RCE 打开环境&#xff0c;源码什么的都没有&#xff0c;扫描后台看看 扫描发现存在git泄露 用githack下载查看得到一串源码 <?php include "flag.php"; echo "flag在哪里呢&#…

go websocket

WebSocket 是一种网络协议&#xff0c;建立在 HTTP 协议之上&#xff0c;允许双向通信。WebSocket 协议允许服务器发送数据到客户端&#xff0c;同时也可以让客户端向服务器发送数据。WebSocket 使用 HTTP 协议的升级请求和响应来建立连接。WebSocket 的主要优点在于它可以通过…

专题十二、字符串

字符串 1. 字符串字面量1.1 字符串字面量中的转义序列1.2 延续字符串字面量1.3 如何存储字符串字面量1.4 字符串字面量的操作1.5 字符串字面量与字符常量 2. 字符串变量2.1 初始化字符串变量2.2 字符数组与字符指针 3. 字符串的读和写3.1 用 printf 函数和 puts 函数写字符串3.…

【Python系列】pydantic版本问题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

jdk和Eclipse软件安装与配置(保姆级别教程)

目录 1、jdk的下载、安装、配置 1.1 jdk安装包的的下载地址&#xff1a;Java Archive | Oracle &#xff0c;点击进入&#xff0c;然后找到你想要的版本下载&#xff0c;如下图&#xff1a; 2.1 开始下载&#xff0c;如下图&#xff1a; 3.1 登入Oracle账号就可以立即下载了…

Docker 搭建私有镜像仓库

一、镜像仓库简介 Docker的镜像仓库是一个用于存储和管理Docker镜像的中央位置。镜像仓库的主要作用是提供一个集中的地方&#xff0c;让用户可以上传、下载、删除和共享Docker镜像。镜像仓库又可以分为公共镜像仓库和私有仓库镜像仓库&#xff1a; 公共镜像仓库 Docker Hub 是…

java Web在线考试管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 在线考试管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使…

网络网络层之(7)PPPOE协议

网络网络层之(7)PPPOE协议 Author: Once Day Date: 2024年4月7日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day…

LeetCode-94(二叉树的中序遍历)

1.递归 时间复杂度O(n) public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res new ArrayList<>();accessTree(root,res);return res;}public void accessTree(TreeNode root,List<Integer>res){if(root null){return;}accessT…

最新剧透前沿信息GPT-5或将今年发布

GPT2 很糟糕 &#xff0c;GPT3 很糟糕 &#xff0c;GPT4 可以 &#xff0c;但 GPT5 会很好。 PS:GPT2 很糟糕,3 很糟糕,4 可以,5 很可以。 如果想升级GPT4玩玩&#xff0c;地址 今年发布的具有推理功能的 GPT5不断发展&#xff0c;就像 iPhone 一样 Sam Altman 于 17 日&am…

OpenAI曾转录100万小时视频数据,训练GPT-4

4月7日&#xff0c;纽约时报在官网发布了一篇名为《科技巨头如何挖空心思&#xff0c;为AI收集数据》的技术文章。 纽约时报表示&#xff0c;OpenAI曾在2021年几乎消耗尽了互联网有用的文本数据源。为了缓解训练数据短缺的难题&#xff0c;便开发了知名开源语音识别模型Whispe…

019——IIC模块驱动开发(基于EEPROM【AT24C02】和I.MX6uLL)

目录 一、 IIC基础知识 二、Linux中的IIC&#xff08;韦东山老师的学习笔记&#xff09; 1. I2C驱动程序的层次 2. I2C总线-设备-驱动模型 2.1 i2c_driver 2.2 i2c_client 三、 AT24C02 介绍 四、 AT24C02驱动开发 实验 驱动程序 应用程序 一、 IIC基础知识 总线类…

Idea中 maven 下载jar出现证书问题

目录 1&#xff1a; 具体错误&#xff1a; 2&#xff1a; 忽略证书代码&#xff1a; 3&#xff1a; 关闭所有idea&#xff0c; 清除缓存&#xff0c; 在下面添加如上忽略证书代码 4&#xff1a;执行 maven clean 然后刷刷新依赖 完成&#xff0c;撒花&#xff01;&#x…