用户登录后端:验签

前端请求拦截器
工具类 cryptomd5Util.js

const crypto = require('crypto')
const publicKey = 'xxxx'
export function encryptedHmacMd5Data(appid, data, timestamp) {const hmac = crypto.createHmac('md5', publicKey)let params = dataif (typeof data === 'object') {for (const i in data) {emptyParams(data, i)}params = JSON.stringify(data)} else if (typeof data === 'string') {params = data.replace(data.match('undefined'), '')}let paramsdata = appid + params + timestampparamsdata = paramsdata.replace(paramsdata.match('undefined'), '')const up = hmac.update(paramsdata, 'utf-8')return up.digest('hex')
}function emptyParams(data, idx) {if (data[idx] === undefined || data[idx] == null) {data[idx] = ''} else if (data[idx] instanceof Array) {for (const i in data[idx]) {emptyParams(data[idx], i)}} else if (typeof data[idx] === 'object') {for (const key in data[idx]) {if (data[idx][key] == null) {data[idx][key] = ''continue}emptyParams(data[idx], key)}}
}export function encryptedMd5(data) {return crypto.createHash('md5').update(data, 'utf-8').digest('hex')
}

request.js(重点看request拦截器)

import axios from 'axios'
import router from '@/router/routers'
import { Notification } from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'
import Config from '@/settings'
import Cookies from 'js-cookie'
import { encryptedHmacMd5Data } from '@/utils/cryptomd5Util'// 创建axios实例
const service = axios.create({baseURL: process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : '/', // api 的 base_urltimeout: Config.timeout // 请求超时时间
})// request拦截器
service.interceptors.request.use(config => {if (getToken()) {config.headers['Authorization'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改}//自定义appidconst systemAppid = 'xxxxx'config.headers['appid'] = systemAppidconst timestamp = new Date().getTime()config.headers['timestamp'] = timestamplet params = ''if (config.method === 'GET' || config.method === 'get') {const arrs0 = JSON.parse(JSON.stringify(config.url))const arrs = arrs0.split('?')const arrs1 = arrs[1] || []if (arrs1.length > 0) {const arrs2 = arrs1.split('&')arrs2.forEach((item) => {const int = item.indexOf('=')const str = item.slice(int + 1)params = params + str})} else {params = undefined}} else {params = config.data}//根据appid,入参,时间戳加密签名config.headers['sign'] = encryptedHmacMd5Data(systemAppid, params, timestamp)config.headers['Content-Type'] = 'application/json'return config},error => {Promise.reject(error)}
)
export default service

后端我也是很清楚,因为用的是springSecurity,看了一个博客看的生无可恋(自己技术太菜)原理解析

就根据在Spring中,Filter对应的Bean为GenericFilterBean。在自己项目搜索

先找到SpringSecurityConfig类 里面有configure方法

/**  Copyright 2019-2020 Zheng Jie**  Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.*  You may obtain a copy of the License at**  http://www.apache.org/licenses/LICENSE-2.0**  Unless required by applicable law or agreed to in writing, software*  distributed under the License is distributed on an "AS IS" BASIS,*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*  See the License for the specific language governing permissions and*  limitations under the License.*/
package com.njry.modules.security.config;import com.njry.annotation.AnonymousAccess;
import com.njry.modules.security.security.JwtAccessDeniedHandler;
import com.njry.modules.security.security.JwtAuthenticationEntryPoint;
import com.njry.modules.security.security.TokenConfigurer;
import com.njry.modules.security.security.TokenProvider;
import com.njry.utils.enums.RequestMethodEnum;
import lombok.RequiredArgsConstructor;
import com.njry.modules.security.config.bean.SecurityProperties;
import com.njry.modules.security.security.*;
import com.njry.modules.security.service.OnlineUserService;
import com.njry.modules.security.service.UserCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.*;/*** @author Zheng Jie*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {private final TokenProvider tokenProvider;private final CorsFilter corsFilter;private final JwtAuthenticationEntryPoint authenticationErrorHandler;private final JwtAccessDeniedHandler jwtAccessDeniedHandler;private final ApplicationContext applicationContext;private final SecurityProperties properties;private final OnlineUserService onlineUserService;private final UserCacheManager userCacheManager;@BeanGrantedAuthorityDefaults grantedAuthorityDefaults() {// 去除 ROLE_ 前缀return new GrantedAuthorityDefaults("");}@Beanpublic PasswordEncoder passwordEncoder() {// 密码加密方式return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 搜寻匿名标记 url: @AnonymousAccessRequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();// 获取匿名标记Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);httpSecurity// 禁用 CSRF.csrf().disable().addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)// 授权异常.exceptionHandling().authenticationEntryPoint(authenticationErrorHandler).accessDeniedHandler(jwtAccessDeniedHandler)// 防止iframe 造成跨域.and().headers().frameOptions().disable()// 不创建会话.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 静态资源等等.antMatchers(HttpMethod.GET,"/*.html","/**/*.html","/**/*.css","/**/*.js","/webSocket/**").permitAll()// swagger 文档.antMatchers("/swagger-ui.html").permitAll().antMatchers("/swagger-resources/**").permitAll().antMatchers("/webjars/**").permitAll().antMatchers("/*/api-docs").permitAll()// 文件.antMatchers("/avatar/**").permitAll().antMatchers("/file/**").permitAll()// 阿里巴巴 druid.antMatchers("/druid/**").permitAll().antMatchers("/druid/**").permitAll()// 放行OPTIONS请求.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型// GET.antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()// POST.antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()// PUT.antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()// PATCH.antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()// DELETE.antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()// 所有类型的接口都放行.antMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()// 所有请求都需要认证.anyRequest().authenticated().and().apply(securityConfigurerAdapter());}private TokenConfigurer securityConfigurerAdapter() {return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager);}private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {Map<String, Set<String>> anonymousUrls = new HashMap<>(8);Set<String> get = new HashSet<>();Set<String> post = new HashSet<>();Set<String> put = new HashSet<>();Set<String> patch = new HashSet<>();Set<String> delete = new HashSet<>();Set<String> all = new HashSet<>();for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {HandlerMethod handlerMethod = infoEntry.getValue();AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);if (null != anonymousAccess) {List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());switch (Objects.requireNonNull(request)) {case GET:get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case POST:post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case PUT:put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case PATCH:patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;case DELETE:delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;default:all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());break;}}}anonymousUrls.put(RequestMethodEnum.GET.getType(), get);anonymousUrls.put(RequestMethodEnum.POST.getType(), post);anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);return anonymousUrls;}
}

接着看到securityConfigurerAdapter方法引用 TokenConfigurer类

/**  Copyright 2019-2020 Zheng Jie**  Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.*  You may obtain a copy of the License at**  http://www.apache.org/licenses/LICENSE-2.0**  Unless required by applicable law or agreed to in writing, software*  distributed under the License is distributed on an "AS IS" BASIS,*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*  See the License for the specific language governing permissions and*  limitations under the License.*/
package com.njry.modules.security.security;import lombok.RequiredArgsConstructor;
import com.njry.modules.security.config.bean.SecurityProperties;
import com.njry.modules.security.service.OnlineUserService;
import com.njry.modules.security.service.UserCacheManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** @author /*/
@RequiredArgsConstructor
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {private final TokenProvider tokenProvider;private final SecurityProperties properties;private final OnlineUserService onlineUserService;private final UserCacheManager userCacheManager;@Overridepublic void configure(HttpSecurity http) {TokenFilter customFilter = new TokenFilter(tokenProvider, properties, onlineUserService, userCacheManager);http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);}
}

因为项目有了jwt的 UsernamePasswordAuthenticationToken(账号密码令牌)
Spring Security对账号密码令牌的支持为:UsernamePasswordAuthenticationToken,想使用该令牌进行身份识别需要在SecurityFilterChain中添加UsernamePasswordAuthenticationFilter

TokenConfigurer类 引用了 TokenFilter

/**  Copyright 2019-2020 Zheng Jie**  Licensed under the Apache License, Version 2.0 (the "License");*  you may not use this file except in compliance with the License.*  You may obtain a copy of the License at**  http://www.apache.org/licenses/LICENSE-2.0**  Unless required by applicable law or agreed to in writing, software*  distributed under the License is distributed on an "AS IS" BASIS,*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*  See the License for the specific language governing permissions and*  limitations under the License.*/
package com.njry.modules.security.security;import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.njry.utils.Hmd5Util;
import io.jsonwebtoken.ExpiredJwtException;
import com.njry.modules.security.config.bean.SecurityProperties;
import com.njry.modules.security.service.UserCacheManager;
import com.njry.modules.security.service.dto.OnlineUserDto;
import com.njry.modules.security.service.OnlineUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;/*** @author /*/
public class TokenFilter extends GenericFilterBean {private static final Logger log = LoggerFactory.getLogger(TokenFilter.class);private final TokenProvider tokenProvider;private final SecurityProperties properties;private final OnlineUserService onlineUserService;private final UserCacheManager userCacheManager;/*** @param tokenProvider     Token* @param properties        JWT* @param onlineUserService 用户在线* @param userCacheManager    用户缓存工具*/public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, OnlineUserService onlineUserService, UserCacheManager userCacheManager) {this.properties = properties;this.onlineUserService = onlineUserService;this.tokenProvider = tokenProvider;this.userCacheManager = userCacheManager;}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;String token = resolveToken(httpServletRequest);
//        log.debug("TOKEN", token);// 对于 Token 为空的不需要去查 Redisif (StrUtil.isNotBlank(token)) {OnlineUserDto onlineUserDto = null;boolean cleanUserCache = false;try {String loginKey = tokenProvider.loginKey(token);onlineUserDto = onlineUserService.getOne(loginKey);} catch (ExpiredJwtException e) {log.error(e.getMessage());cleanUserCache = true;} finally {if (cleanUserCache || Objects.isNull(onlineUserDto)) {userCacheManager.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY)));}}if (onlineUserDto != null && StringUtils.hasText(token)) {Authentication authentication = tokenProvider.getAuthentication(token);SecurityContextHolder.getContext().setAuthentication(authentication);// Token 续期tokenProvider.checkRenewal(token);}}filterChain.doFilter(servletRequest, servletResponse);/* String requestURL = httpServletRequest.getRequestURI();Set<String> unfilterUrls = properties.getUnFilterUrl();if(isPass(unfilterUrls,requestURL)){filterChain.doFilter(servletRequest, servletResponse);}else{Map resultMap = chkSign(httpServletRequest,httpServletResponse);
//            boolean flag = (boolean)resultMap.get("flag");boolean flag = true;RequestWrapper requestWrapper = (RequestWrapper)resultMap.get("requestWrapper");System.out.println("==============flag======="+(flag?"Y":"N"));if(!flag){httpServletResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "错误的请求");httpServletResponse.addHeader("SC_BAD_REQUEST","错误的请求");return;}if(requestWrapper==null){filterChain.doFilter(servletRequest, servletResponse);}else{filterChain.doFilter(requestWrapper, servletResponse);}}*/}/*** 初步检测Token** @param request /* @return /*/private String resolveToken(HttpServletRequest request) {String bearerToken = request.getHeader(properties.getHeader());if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) {// 去掉令牌前缀return bearerToken.replace(properties.getTokenStartWith(), "");} else {log.debug("非法Token:{}", bearerToken);}return null;}/*** 初步检测Token** @param request /* @return /*/private String resolveHeadParam(HttpServletRequest request,String paramName) {String param = request.getHeader(paramName);if (StringUtils.hasText(param) ) {// 去掉令牌前缀return param.replace(" ", "");} else {log.debug("非法参数:{}", paramName);}return null;}public Map chkSign(HttpServletRequest request, HttpServletResponse response) throws IOException {String method = request.getMethod();String sign = resolveHeadParam(request,"Sign");String appId = resolveHeadParam(request,"Appid");String timestamp = resolveHeadParam(request,"Timestamp");int  reqTime = properties.getReqTime();Map resultMap = new HashMap();resultMap.put("flag",true);String ipAddr = com.njry.utils.StringUtils.getIp(request);if (method.equals("GET")){Map<String, String[]> paramsMap = request.getParameterMap();Object[] keyObjs = paramsMap.keySet().toArray();Arrays.sort(keyObjs);StringBuffer sb = new StringBuffer();for(Object paramkey : keyObjs){if (com.njry.utils.StringUtils.notEmpty(paramkey).equals("sign") ) {continue;}String[] paramsValues = paramsMap.get(paramkey);if (null == paramsValues) {paramsValues = new String[]{};}sb.append(com.njry.utils.StringUtils.arrayToStringNoTrim(paramsValues, ""));}if (appId==null){resultMap.put("flag",false);}else {String key = null;String secretKey = "xxxx";if (secretKey==null){resultMap.put("flag",false);}key  = Hmd5Util.encryptData(secretKey,appId + sb.toString() + timestamp);System.out.println("appId + sb.toString() + timestamp====》"+appId + sb.toString() + timestamp);//需要加密内容String paramsValue = appId + sb.toString() + timestamp;//加密获取加密数据String smallKey = key.toLowerCase();String smallSign = sign.toLowerCase();System.out.println("==============smallKey======="+smallKey);System.out.println("==============smallSign======"+smallSign);long curTime = System.currentTimeMillis();System.out.println("==============curTime======"+curTime);System.out.println("==============timestamp======"+(curTime - Long.parseLong(timestamp)));System.out.println("==============reqTime======"+reqTime);if (curTime - Long.parseLong(timestamp) > reqTime) {System.out.println("==============FFFFF======");resultMap.put("flag",false);}}}else {//获取请求参数RequestWrapper requestWrapper = new RequestWrapper(request);resultMap.put("requestWrapper",requestWrapper);String data  =   requestWrapper.getBodyString();System.out.println("==========data====》"+data);if(data==null){data = "";}if (appId==null){resultMap.put("flag",false);}else {String key = null;String secretKey = "xxxx";if (secretKey==null){resultMap.put("flag",false);}//加密获取加密数据key  = Hmd5Util.encryptData( secretKey,appId+data+timestamp);System.out.println("appId + data+ timestamp====》"+appId + data + timestamp);//需要加密内容String paramsValue = appId + data + timestamp;String smallKey = key.toLowerCase();String smallSign = sign.toLowerCase();System.out.println("==============smallKey======="+smallKey);System.out.println("==============smallSign======"+smallSign);if (!smallKey.equals(smallSign)){resultMap.put("flag",false);}long curTime = System.currentTimeMillis();if (curTime - Long.parseLong(timestamp) > reqTime){resultMap.put("flag",false);}}}return resultMap;}public boolean isPass(Set<String> unFilterSet, String requestURI){logger.info("=====requestURI = "+requestURI);if(unFilterSet != null && unFilterSet.size() > 0){for (String unFilterUri : unFilterSet) {//logger.info("=====unFilterUri = "+unFilterUri);if(!org.apache.commons.lang.StringUtils.isBlank(unFilterUri)){unFilterUri = unFilterUri.trim();if(unFilterUri.equals(requestURI)){return true;}else if(unFilterUri.startsWith("*") && unFilterUri.length() > 1 && unFilterUri.endsWith("*")){String text = unFilterUri.substring(1, (unFilterUri.length() - 1));//logger.info("=====contains text = " + text);if(requestURI.contains(text)){return true;}}else if(unFilterUri.startsWith("*") && !unFilterUri.endsWith("*")){String text = unFilterUri.substring(1, (unFilterUri.length()));//logger.info("=====endsWith text = " + text);if(requestURI.endsWith(text)){return true;}}else if(!unFilterUri.startsWith("*") && unFilterUri.endsWith("*")){String text = unFilterUri.substring(0, (unFilterUri.length() - 1));//logger.info("=====startsWith text = " + text);if(requestURI.startsWith(text)){return true;}}}}}return false;}
}

这里先找redis里面有没有在线用户和缓存是否存在
下面验签注释了chkSign方法
一堆验证,里面有对appid的解密

package com.njry.utils;import org.springframework.stereotype.Component;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;@Component
public class Hmd5Util {public static final String KEY_MAC = "HmacMD5";//密钥private static String key = "密钥";public static String encryptData(String KEY,String content){try{SecretKeySpec secretKey = new SecretKeySpec(KEY.getBytes("UTF-8"), KEY_MAC);Mac mac= Mac.getInstance(KEY_MAC);mac.init(secretKey);mac.update(content.getBytes("UTF-8"));return toHex(mac.doFinal());}catch (Exception e) {e.printStackTrace();}return "";}//转16进制字符串public static String toHex(byte[] bytes) {BigInteger bi = new BigInteger(1, bytes);return String.format("%0" + (bytes.length << 1) + "X", bi);}public static void main(String[] args) {}
}

在这里插入图片描述

总结

云里雾去

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

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

相关文章

Axure RP移动端交互元件库/交互原型模板

作品类型&#xff1a;元件库/原型模板 更新日期&#xff1a;2023-12-04 当前版本&#xff1a;V1.3 适用范围&#xff1a;App应用/小程序 Axure版本&#xff1a;Axure 9.0均可打开 文件大小&#xff1a;36.7M 历时两个月制作并整理了手机移动端常用的75种组件、90个常用界面模板…

电影网站|基于SSM+vue的电影网站系统(源码+数据库+文档)

电影网站 目录 基于SSMvue的电影网站系统 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2 管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道…

Kafka和Spark Streaming的组合使用学习笔记(Spark 3.5.1)

一、安装Kafka 1.执行以下命令完成Kafka的安装&#xff1a; cd ~ //默认压缩包放在根目录 sudo tar -zxf kafka_2.12-2.6.0.tgz -C /usr/local cd /usr/local sudo mv kafka_2.12-2.6.0 kafka-2.6.0 sudo chown -R qiangzi ./kafka-2.6.0 二、启动Kafaka 1.首先需要启动K…

CentOS 磁盘扩容与创建分区

文章目录 未分配空间创建新分区重启服务器添加物理卷扩展逻辑卷 操作前确认已给服务器增加硬盘或虚拟机已修改硬盘大小&#xff08;必须重启服务才会生效&#xff09;。 未分配空间 示例说明&#xff1a;原服务器只有40G&#xff0c;修改虚拟机硬盘大小再增加20G后硬盘变为60G。…

macOS上将ffmpeg.c编译成Framework

1 前言 本文介绍下在macOS上将ffmpeg的fftools目录下的ffmpeg.c程序&#xff0c;也就是ffmpeg的命令行程序&#xff0c;编译成framework的方法。编译成.a或.dylib的方法类似。 编译环境如下&#xff1a; xcode15.3&#xff1b;ffmpeg release/6.1; 2 编译ffmpeg 首先clone我们…

stl学习以及abc比赛例题

1.引例 一提到查找&#xff0c;我们一上来想的肯定是find()函数或者search()函数&#xff0c;但是这种查找的底层逻辑终究是用顺序查找的方式&#xff0c;运行的时间成本非常高昂&#xff0c;所以平时能不用就不用&#xff0c;比赛的时候用这种查找和自己while遍历&#xff0c…

Maven 的仓库、周期和插件

优质博文&#xff1a;IT-BLOG-CN 一、Maven 仓库 在Maven的世界中&#xff0c;任何一个依赖、插件或者项目构建的输出&#xff0c;都可以称为构建。Maven在某个统一的位置存储所有项目的共享的构建&#xff0c;这个统一的位置&#xff0c;我们就称之为仓库。任何的构建都有唯一…

基于Java+SpringBoot+Mybaties-plus+Vue+elememt 驾校管理 设计与实现

一.项目介绍 系统角色&#xff1a;管理员、驾校教练、学员 管理员&#xff1a; 个人中心&#xff1a;修改密码以及个人信息修改 学员管理&#xff1a;维护学员信息&#xff0c;维护学员成绩信息 驾校教练管理&#xff1a;驾校教练信息的维护 驾校车辆管理&…

从0开发、发布油猴脚本(保姆级)

概览 项目中使用conify集成图标&#xff0c;有些内网用户只能使用离线图标&#xff0c;但是如何判断使用的conify集成图标是在线还是离线呢&#xff1f;这个时候就需要一个油猴脚本&#xff0c;作用于iconify官网&#xff0c;对离线图标进行标识。 此篇文章主要从如下几点去梳…

ITMS-91053: Missing API declaration

1. 添加PrivacyInfo.xcprivacy File → New → File → App Privacy 2. 格式 3. 已知对应关系 NSPrivacyAccessedAPICategoryFileTimestamp 3B52.1: Files provided to app by user, per documentation&#xff1a;File Timestamp NSPrivacyAccessedAPICategoryDiskSpace …

企业设置,支持自定义短信签名

05/08 主要更新模块概览 自动换行 启动封面 使用统计 短信签名 01表单管理 1.1 【表单外链】- 查询外链支持多个外链 说明&#xff1a; 表单查询外链原仅支持一个&#xff0c;现支持增加多个外链功能&…

从loss角度理解LLM涌现能力

如今的很多研究都表明小模型也能出现涌现能力&#xff0c;本文的作者团队通过大量实验发现模型的涌现能力与模型大小、训练计算量无关&#xff0c;只与预训练loss相关。 作者团队惊奇地发现&#xff0c;不管任何下游任务&#xff0c;不管模型大小&#xff0c;模型出现涌现能力…