当前位置: 首页 > news >正文

【项目】springboot中使用kaptcha生成验证码,登录时密码加盐处理

记录使用kaptcha的过程
为前后端分离项目,前端vue框架

0.下载jar包

在maven的网站https://mvnrepository.com/search?q=com.github.penggle
搜索下载jar
在这里插入图片描述
导入到工程中

1.添加依赖

<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>5.4.1</version></dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.1</version></dependency><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-core</artifactId><version>2.1.14</version></dependency><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency>

后面的依赖是因为测试的时候有报错添加的,以防万一可以添加一下

2.添加KaptchaConfig配置

package com.louis.mango.config;import java.util.Properties;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;@Configuration
public class KaptchaConfig {@Beanpublic DefaultKaptcha producer() {Properties properties = new Properties();properties.put("kaptcha.border", "no");properties.put("kaptcha.textproducer.font.color", "black");properties.put("kaptcha.textproducer.char.space", "5");Config config = new Config(properties);DefaultKaptcha defaultKaptcha = new DefaultKaptcha();defaultKaptcha.setConfig(config);return defaultKaptcha;}
}

注意注解 @Bean,不添加这个注解的话后面注入时会报无法注入。

3.后端用于登录的封装类编写

LoginBean:

public class LoginBean {private String account;private String password;private String captcha;public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getCaptcha() {return captcha;}public void setCaptcha(String captcha) {this.captcha = captcha;}}

4.修改controller层代码与前端交互

后端:

@RestController
public class SysLoginController {@Autowiredprivate Producer producer;@Autowiredprivate SysUserService sysUserService;@Autowiredprivate AuthenticationManager authenticationManager;@GetMapping("captcha.jpg")public void captcha(HttpServletResponse response, HttpServletRequest request) throws ServletException, IOException {response.setHeader("Cache-Control", "no-store, no-cache");response.setContentType("image/jpeg");// 生成文字验证码String text = producer.createText();// 生成图片验证码BufferedImage image = producer.createImage(text);// 保存到验证码到 sessionrequest.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, text);ServletOutputStream out = response.getOutputStream();ImageIO.write(image, "jpg", out);	IOUtils.closeQuietly(out);}/*** 登录接口*/@PostMapping(value = "/login")//public HttpResultpublic String login(@RequestBody LoginBean loginBean, HttpServletRequest request) throws IOException {String username = loginBean.getAccount();String password = loginBean.getPassword();String captcha = loginBean.getCaptcha();// 从session中获取之前保存的验证码跟前台传来的验证码进行匹配Object kaptcha = request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);if (kaptcha == null) {//return HttpResult.error("验证码已失效");return "已经失效";}if (!captcha.equals(kaptcha)) {return "验证码不正确";//return HttpResult.error("验证码不正确");}// 用户信息SysUser user = sysUserService.findByName(username);System.out.println(user);// 账号不存在、密码错误if (user == null) {//return HttpResult.error("账号不存在");return "账号不存在";}if (!PasswordUtils.matches(user.getSalt(), password, user.getPassword())) {return "密码不正确";}// 账号锁定if (user.getStatus() == 0) {return "账号已被锁定,请联系管理员";}// 系统登录认证JwtAuthenticatioToken token = SecurityUtils.login(request, username, password, authenticationManager);System.out.println("yes!");System.out.println(token);
//
//		return HttpResult.ok(token);
//	}return "ok";}}

前端:

template:

<template><el-form :model="loginForm" :rules="fieldRules" ref="loginForm" label-position="left" label-width="0px" class="demo-ruleForm login-container"><span class="tool-bar"></span><h2 class="title" style="padding-left:22px;" >系统登录</h2><el-form-item prop="account"><el-input type="text" v-model="loginForm.account" auto-complete="off" placeholder="账号"></el-input></el-form-item><el-form-item prop="password"><el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码"></el-input></el-form-item><el-form-item ><el-col :span="12"><el-form-item prop="captcha"><el-input type="test" v-model="loginForm.captcha" auto-complete="off" placeholder="验证码, 单击图片刷新"style="width: 100%;"></el-input></el-form-item></el-col><el-col class="line" :span="1">&nbsp;</el-col><el-col :span="11"><el-form-item><img style="width: 100%;" class="pointer" :src="loginForm.src" @click="refreshCaptcha"></el-form-item></el-col></el-form-item><el-form-item style="width:100%;"><el-button type="primary" style="width:48%;" @click.native.prevent="reset">重 置</el-button><el-button type="primary" style="width:48%;" @click.native.prevent="login" :loading="loading">登 录</el-button></el-form-item></el-form>
</template>

script:

export default {data() {return {loading: false,loginForm: {account: 'admin',password: 'admin',captcha:'',src: ''},fieldRules: {account: [{ required: true, message: '请输入账号', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' }]},checked: true}},methods: {login() {this.loading = truelet userInfo = { account:this.loginForm.account, password:this.loginForm.password,captcha:this.loginForm.captcha }this.$api.login.login(userInfo).then((res) => {if (res == "ok" ) {window.sessionStorage.setItem('flag','ok'); // session 放置-->this.$message.success("登陆成功!!!");this.$router.push({ path: "/home"});}this.loading = false}).catch((res) => {this.$message({ message: res.message, type: 'error' })})},refreshCaptcha: function(){this.loginForm.src = "http://localhost:8001/captcha.jpg?t=" + new Date().getTime();},reset() {this.$refs.loginForm.resetFields()}mounted() {this.refreshCaptcha()}
}

关于其中api:

目录:
(src下)
在这里插入图片描述
index.js:

import api from './api'const install = Vue => {if (install.installed)return;install.installed = true;Object.defineProperties(Vue.prototype, {// 注意,此处挂载在 Vue 原型的 $api 对象上$api: {get() {return api}}})
}export default install

login.js:

import axios from '../axios'/* * 系统登录模块*/// 登录
export const login = data => {return axios({url: 'login',method: 'post',data})
}// 登出
export const logout = () => {return axios({url: 'logout',method: 'get'})
}

axios.js:

import axios from 'axios';
import config from './config';
import Cookies from "js-cookie";
import router from '@/router'export default function $axios(options) {return new Promise((resolve, reject) => {const instance = axios.create({baseURL: config.baseUrl,headers: config.headers,timeout: config.timeout,withCredentials: config.withCredentials})// request 请求拦截器instance.interceptors.request.use(config => {let token = Cookies.get('token')// 发送请求时携带tokenif (token) {config.headers.token = token} else {// 重定向到登录页面router.push('/login')}return config},error => {// 请求发生错误时console.log('request:', error)// 判断请求超时if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {console.log('timeout请求超时')}// 需要重定向到错误页面const errorInfo = error.responseconsole.log(errorInfo)if (errorInfo) {error = errorInfo.data  // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.rejectconst errorStatus = errorInfo.status; // 404 403 500 ...router.push({path: `/error/${errorStatus}`})}return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息})// response 响应拦截器instance.interceptors.response.use(response => {return response.data},err => {if (err && err.response) {switch (err.response.status) {case 400:err.message = '请求错误'breakcase 401:err.message = '未授权,请登录'breakcase 403:err.message = '拒绝访问'breakcase 404:err.message = `请求地址出错: ${err.response.config.url}`breakcase 408:err.message = '请求超时'breakcase 500:err.message = '服务器内部错误'breakcase 501:err.message = '服务未实现'breakcase 502:err.message = '网关错误'breakcase 503:err.message = '服务不可用'breakcase 504:err.message = '网关超时'breakcase 505:err.message = 'HTTP版本不受支持'breakdefault:}}console.error(err)return Promise.reject(err) // 返回接口返回的错误信息})// 请求处理instance(options).then(res => {resolve(res)return false}).catch(error => {reject(error)})})
}

在这里插入图片描述

mock目录下的index.js:

import Mock from 'mockjs'import * as login from './modules/login'
import * as user from './modules/user'
import * as role from './modules/role'
import * as dept from './modules/dept'
import * as menu from './modules/menu'
import * as dict from './modules/dict'
import * as config from './modules/config'
import * as log from './modules/log'
import * as loginlog from './modules/loginlog'// 1. 开启/关闭[所有模块]拦截, 通过调[openMock参数]设置.
// 2. 开启/关闭[业务模块]拦截, 通过调用fnCreate方法[isOpen参数]设置.
// 3. 开启/关闭[业务模块中某个请求]拦截, 通过函数返回对象中的[isOpen属性]设置.
let openMock = true
//let openMock = false
fnCreate(user, openMock)
fnCreate(role, openMock)
fnCreate(dept, openMock)
fnCreate(menu, openMock)
fnCreate(dict, openMock)
fnCreate(config, openMock)
fnCreate(log, openMock)
fnCreate(loginlog, openMock)
fnCreate(login, openMock)/*** 创建mock模拟数据* @param {*} mod 模块* @param {*} isOpen 是否开启?*/
function fnCreate (mod, isOpen = true) {if (isOpen) {for (var key in mod) {((res) => {if (res.isOpen !== false) {let url = "http://localhost:8001/"if(!url.endsWith("/")) {url = url + "/"}url = url + res.urlMock.mock(new RegExp(url), res.type, (opts) => {opts['data'] = opts.body ? JSON.parse(opts.body) : nulldelete opts.bodyconsole.log('\n')console.log('%cmock拦截, 请求: ', 'color:blue', opts)console.log('%cmock拦截, 响应: ', 'color:blue', res.data)return res.data})}})(mod[key]() || {})}}
}

mock目录下的login.js:

/* * 系统登录模块*/// 登录接口
export function login() {const loginData = {"code": 200,"msg": null,"data": {"authorities": [],"details": {"remoteAddress": "0:0:0:0:0:0:0:1","sessionId": "E9E774A8EB4405B25692D84B4521CB45"},"authenticated": false,"principal": "admin","credentials": "admin","token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU0NzkzMTYxOCwiY3JlYXRlZCI6MTU0Nzg4ODQxODgyMiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6InN5czptZW51OmRlbGV0ZSJ9LHsiYXV0aG9yaXR5Ijoic3lzOmRpY3Q6ZWRpdCJ9LHsiYXV0aG9yaXR5Ijoic3lzOmRpY3Q6ZGVsZXRlIn0seyJhdXRob3JpdHkiOiJzeXM6Y29uZmlnOmFkZCJ9LHsiYXV0aG9yaXR5Ijoic3lzOm1lbnU6YWRkIn0seyJhdXRob3JpdHkiOiJzeXM6dXNlcjphZGQifSx7ImF1dGhvcml0eSI6InN5czpkZXB0OmRlbGV0ZSJ9LHsiYXV0aG9yaXR5Ijoic3lzOnJvbGU6ZWRpdCJ9LHsiYXV0aG9yaXR5Ijoic3lzOnJvbGU6dmlldyJ9LHsiYXV0aG9yaXR5Ijoic3lzOmRpY3Q6dmlldyJ9LHsiYXV0aG9yaXR5Ijoic3lzOnVzZXI6ZGVsZXRlIn0seyJhdXRob3JpdHkiOiJzeXM6ZGVwdDp2aWV3In0seyJhdXRob3JpdHkiOiJzeXM6bWVudTp2aWV3In0seyJhdXRob3JpdHkiOiJzeXM6ZGljdDphZGQifSx7ImF1dGhvcml0eSI6InN5czpyb2xlOmFkZCJ9LHsiYXV0aG9yaXR5Ijoic3lzOnVzZXI6dmlldyJ9LHsiYXV0aG9yaXR5Ijoic3lzOmRlcHQ6ZWRpdCJ9LHsiYXV0aG9yaXR5Ijoic3lzOmNvbmZpZzplZGl0In0seyJhdXRob3JpdHkiOiJzeXM6bG9naW5sb2c6dmlldyJ9LHsiYXV0aG9yaXR5Ijoic3lzOnVzZXI6ZWRpdCJ9LHsiYXV0aG9yaXR5Ijoic3lzOmNvbmZpZzp2aWV3In0seyJhdXRob3JpdHkiOiJzeXM6Y29uZmlnOmRlbGV0ZSJ9LHsiYXV0aG9yaXR5Ijoic3lzOmRlcHQ6YWRkIn0seyJhdXRob3JpdHkiOiJzeXM6cm9sZTpkZWxldGUifSx7ImF1dGhvcml0eSI6InN5czptZW51OmVkaXQifV19.Lw2qb2BJHwmiVMHS_vbaLf7vnTT6frr7vTS2-nJ1Lo0uOduqK6nPtBtgEka-fH7ow-s5n7OH1WZkUvH0PN6oyA","name": "admin"}}return {url: 'login',type: 'post',data: loginData}
}
// 登出接口
export function logout() {const logoutData = {"code": 200,"msg": null,"data": {}}return {url: 'logout',type: 'get',data: logoutData}
}

5.密码加盐和token相关的代码

目录:
在这里插入图片描述

PasswordUtils :

public class PasswordUtils {/*** 匹配密码* @param salt 盐* @param rawPass 明文 * @param encPass 密文* @return*/public static boolean matches(String salt, String rawPass, String encPass) {return new PasswordEncoder(salt).matches(encPass, rawPass);}/*** 明文密码加密* @param rawPass 明文* @param salt* @return*/public static String encode(String rawPass, String salt) {return new PasswordEncoder(salt).encode(rawPass);}/*** 获取加密盐* @return*/public static String getSalt() {return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);}
}
  • 密码加密 PasswordEncoder
public class PasswordEncoder {private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d","e", "f" };private final static String MD5 = "MD5";private final static String SHA = "SHA";private Object salt;private String algorithm;public PasswordEncoder(Object salt) {this(salt, MD5);}public PasswordEncoder(Object salt, String algorithm) {this.salt = salt;this.algorithm = algorithm;}/*** 密码加密* @param rawPass* @return*/public String encode(String rawPass) {String result = null;try {MessageDigest md = MessageDigest.getInstance(algorithm);// 加密后的字符串result = byteArrayToHexString(md.digest(mergePasswordAndSalt(rawPass).getBytes("utf-8")));} catch (Exception ex) {}return result;}/*** 密码匹配验证* @param encPass 密文* @param rawPass 明文* @return*/public boolean matches(String encPass, String rawPass) {String pass1 = "" + encPass;String pass2 = encode(rawPass);return pass1.equals(pass2);}private String mergePasswordAndSalt(String password) {if (password == null) {password = "";}if ((salt == null) || "".equals(salt)) {return password;} else {return password + "{" + salt.toString() + "}";}}/*** 转换字节数组为16进制字串* * @param b*            字节数组* @return 16进制字串*/private String byteArrayToHexString(byte[] b) {StringBuffer resultSb = new StringBuffer();for (int i = 0; i < b.length; i++) {resultSb.append(byteToHexString(b[i]));}return resultSb.toString();}/*** 将字节转换为16进制* @param b* @return*/private static String byteToHexString(byte b) {int n = b;if (n < 0)n = 256 + n;int d1 = n / 16;int d2 = n % 16;return hexDigits[d1] + hexDigits[d2];}
  • Security相关操作
public class SecurityUtils {/*** 系统登录认证* @param request* @param username* @param password* @param authenticationManager* @return*/public static JwtAuthenticatioToken login(HttpServletRequest request, String username, String password, AuthenticationManager authenticationManager) {JwtAuthenticatioToken token = new JwtAuthenticatioToken(username, password);token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));// 执行登录认证过程Authentication authentication = authenticationManager.authenticate(token);// 认证成功存储认证信息到上下文SecurityContextHolder.getContext().setAuthentication(authentication);// 生成令牌并返回给客户端token.setToken(JwtTokenUtils.generateToken(authentication));return token;}/*** 获取令牌进行认证* @param request*/public static void checkAuthentication(HttpServletRequest request) {// 获取令牌并根据令牌获取登录认证信息Authentication authentication = JwtTokenUtils.getAuthenticationeFromToken(request);// 设置登录认证信息到上下文SecurityContextHolder.getContext().setAuthentication(authentication);}/*** 获取当前用户名* @return*/public static String getUsername() {String username = null;Authentication authentication = getAuthentication();if(authentication != null) {Object principal = authentication.getPrincipal();if(principal != null && principal instanceof UserDetails) {username = ((UserDetails) principal).getUsername();}}return username;}/*** 获取用户名* @return*/public static String getUsername(Authentication authentication) {String username = null;if(authentication != null) {Object principal = authentication.getPrincipal();if(principal != null && principal instanceof UserDetails) {username = ((UserDetails) principal).getUsername();}}return username;}/*** 获取当前登录信息* @return*/public static Authentication getAuthentication() {if(SecurityContextHolder.getContext() == null) {return null;}Authentication authentication = SecurityContextHolder.getContext().getAuthentication();return authentication;}}
  • JWT工具类
public class JwtTokenUtils implements Serializable {private static final long serialVersionUID = 1L;/*** 用户名称*/private static final String USERNAME = Claims.SUBJECT;/*** 创建时间*/private static final String CREATED = "created";/*** 权限列表*/private static final String AUTHORITIES = "authorities";/*** 密钥*/private static final String SECRET = "abcdefgh";/*** 有效期12小时*/private static final long EXPIRE_TIME = 12 * 60 * 60 * 1000;/*** 生成令牌** @return 令牌*/public static String generateToken(Authentication authentication) {Map<String, Object> claims = new HashMap<>(3);claims.put(USERNAME, SecurityUtils.getUsername(authentication));claims.put(CREATED, new Date());claims.put(AUTHORITIES, authentication.getAuthorities());return generateToken(claims);}/*** 从数据声明生成令牌** @param claims 数据声明* @return 令牌*/private static String generateToken(Map<String, Object> claims) {Date expirationDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();}/*** 从令牌中获取用户名** @param token 令牌* @return 用户名*/public static String getUsernameFromToken(String token) {String username;try {Claims claims = getClaimsFromToken(token);username = claims.getSubject();} catch (Exception e) {username = null;}return username;}/*** 根据请求令牌获取登录认证信息* @return 用户名*/public static Authentication getAuthenticationeFromToken(HttpServletRequest request) {Authentication authentication = null;// 获取请求携带的令牌String token = JwtTokenUtils.getToken(request);if(token != null) {// 请求令牌不能为空if(SecurityUtils.getAuthentication() == null) {// 上下文中Authentication为空Claims claims = getClaimsFromToken(token);if(claims == null) {return null;}String username = claims.getSubject();if(username == null) {return null;}if(isTokenExpired(token)) {return null;}Object authors = claims.get(AUTHORITIES);List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();if (authors != null && authors instanceof List) {for (Object object : (List) authors) {authorities.add(new GrantedAuthorityImpl((String) ((Map) object).get("authority")));}}authentication = new JwtAuthenticatioToken(username, null, authorities, token);} else {if(validateToken(token, SecurityUtils.getUsername())) {// 如果上下文中Authentication非空,且请求令牌合法,直接返回当前登录认证信息authentication = SecurityUtils.getAuthentication();}}}return authentication;}/*** 从令牌中获取数据声明** @param token 令牌* @return 数据声明*/private static Claims getClaimsFromToken(String token) {Claims claims;try {claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();} catch (Exception e) {claims = null;}return claims;}/*** 验证令牌* @param token* @param username* @return*/public static Boolean validateToken(String token, String username) {String userName = getUsernameFromToken(token);return (userName.equals(username) && !isTokenExpired(token));}/*** 刷新令牌* @param token* @return*/public static String refreshToken(String token) {String refreshedToken;try {Claims claims = getClaimsFromToken(token);claims.put(CREATED, new Date());refreshedToken = generateToken(claims);} catch (Exception e) {refreshedToken = null;}return refreshedToken;}/*** 判断令牌是否过期** @param token 令牌* @return 是否过期*/public static Boolean isTokenExpired(String token) {try {Claims claims = getClaimsFromToken(token);Date expiration = claims.getExpiration();return expiration.before(new Date());} catch (Exception e) {return false;}}/*** 获取请求token* @param request* @return*/public static String getToken(HttpServletRequest request) {String token = request.getHeader("Authorization");String tokenHead = "Bearer ";if(token == null) {token = request.getHeader("token");} else if(token.contains(tokenHead)){token = token.substring(tokenHead.length());} if("".equals(token)) {token = null;}return token;}}

6.测试

在这里插入图片描述
在这里插入图片描述
后端打印的内容:
在这里插入图片描述

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

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

Yocto Build with Bitbake的调试

https://docs.yoctoproject.org/dev-manual/common-tasks.html#recipe-logging-mechanisms 官方文档里有输出调试信息的方法。包含两种&#xff0c;一种是调用python的&#xff0c;一种是bash的。 建议直接使用warning方法&#xff0c;保证输出级别足够。 另外&#xff0c;在…...

IntentService源码分析指南

转载链接&#xff1a;https://www.jianshu.com/p/8a3c44a9173a...

为啥集群小文件治理那么重要,你真的懂吗?

小文件是 Hadoop 集群运维中的常见挑战&#xff0c;尤其对于大规模运行的集群来说可谓至关重要。如果处理不好&#xff0c;可能会导致许多并发症。Hadoop集群本质是为了TB,PB规模的数据存储和计算因运而生的。为啥大数据开发都说小文件的治理重要&#xff0c;说HDFS 存储小文件…...

Flink大数据实时计算系列-第一个程序JAVA版本-分组统计

Flink大数据实时计算系列-第一个程序JAVA版本-分组 在基线版本上增加了分组统计的代码 #Flink代码 package com.flinkniu.flink.operator;import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink...

Ant Design Vue实现a-table表格单元格合并

Ant Design Vue实现a-table表格单元格合并 参考Ant design Vue官网关于行和列合并api https://antdv.com/components/table-cn/ 下面官网对表格合并这块的描述 &#xff01;&#xff01;&#xff01;注意注意注意重要事情说三遍&#xff1a;很多同学都习惯性的将columns写在d…...

css中id和class的区别

css中id和class的区别ID是唯一的类不是唯一的语法区别ID是唯一的 每个元素仅可以有一个ID每个页面仅可以有一个元素拥有这个ID 类不是唯一的 你可以在多个元素中使用相同的类你可以使用多个类在一个元素中 语法区别 id对应css是用样式选择符“#”&#xff08;井号&#xff…...

力扣刷题:动态规划篇

目录322. 零钱兑换题目介绍题目实现5. 最长回文子串题目介绍题目实现518. 零钱兑换 II题目介绍题目实现53. 最大子序和题目介绍题目实现63. 不同路径 II题目介绍[62. 不同路径](https://leetcode-cn.com/problems/unique-paths/)题目实现70. 爬楼梯题目介绍题目实现72. 编辑距离…...

Java面试list 和 set 区别

List和Set的区别 List:&#xff1a;有序&#xff0c;按对象进入的的序保存对象&#xff0c;可可允许多个Null元素对象&#xff0c;可以使用lterator迭取出所有元素。在逐一便历&#xff0c;也可以使用get(int index)获取指定下标的元素 Set&#xff1a;无序&#xff0c;不可重复…...

诚意分享:这里有份超全Java体系化进阶学习图谱

摘要 Apache Kafka是一个分布式消息发布订阅系统。它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log)&#xff0c;之后成为Apache项目的一部分。 成千上万的企业都在使用Kafka&#xff0c;三分之一的世界500强公司也在其中&…...

Twitter教程:如何下载Twitter数据副本?

Twitterrific Mac破解版是一款非常现代化优秀的桌面Twitter客户端,这款软件不仅看起来惊人,它的表现也是非常棒的,本文给大家的带来了如何下载Twitter数据副本的教程。 教程如下 在 iPhone 或 iPad 上打开“Twitter”。&#xff08;您也可以使用 Twitter.com 网站&#xff09…...

【内网学习笔记】8、powercat 的使用

1、下载安装 powercat powercat 可以视为 nc 的 powershell 版本&#xff0c;因此也可以和 nc 进行连接。 powercat 可在 github 进行下载&#xff0c;项目地址为&#xff1a;https://github.com/besimorhino/powercat 下载下来 powercat.ps1 文件后&#xff0c;直接导入即可…...

栈和队列(二) : 用栈实现队列

leetcode232.用栈实现队列 https://leetcode-cn.com/problems/implement-queue-using-stacks/ 使用栈实现队列的下列操作&#xff1a; push(x) – 将一个元素放入队列的尾部。 pop() – 从队列首部移除元素。 peek() – 返回队列首部的元素。 empty() – 返回队列是否为空。…...

测试技巧:弱网测试

弱网测试场景 当前APP网络环境比较复杂&#xff0c;网络制式有2G、3G、4G网络&#xff0c;还有越来越多的公共Wi-Fi。不同的网络环境和网络制式的差异&#xff0c;都会对用户使用app造成一定影响。另外&#xff0c;当前app使用场景多变&#xff0c;如进地铁、上公交、进电梯等…...

PMP哪里报名

首先了解下PMP考试时间&#xff0c;一年四次&#xff0c;正常情况是每年3、6、9、12月份考试&#xff1b; 其次了解PMP考试需要两次报名&#xff0c;分别是英文报名和中文报名&#xff1b;且两次报名通过后&#xff0c;才能正常考试。 下面分别介绍PMP英文报名和PMP考试中文报…...

Thread类的常用方法

Thread类的常用方法 void start(): 启动线程&#xff0c;并执行对象的run()方法run(): 线程在被调度时执行的操作static Thread currentThread()&#xff1a; 返回当前线程。在Thread子类中就 是this&#xff0c;通常用于主线程和Runnable实现类String getName(): 返回线程的名…...

浅谈设计模式(三)

前言 之前详细介绍了几种常用的设计模式&#xff0c;最后总结一下附上所有设计模式的类图以及六大设计原则 一、创建型 1.Factory Method&#xff08;工厂方法&#xff09; 定义&#xff1a;定义了一个创建对象的接口&#xff0c;但由子类决定要实例化的类是哪一个。工厂方法…...

AR增强现实让科技贴近生活

AR也叫增强现实&#xff0c;它是在1990年被正式提出的&#xff0c;在时间上要比VR虚拟现实技术晚一些&#xff0c;它的实现主要通过三维空间、场景交融、现实视频等技术相互作用、融合实现的。 AR增强现实技术在工业领域中&#xff0c;有着超强的适用性&#xff0c;假设某件工…...

c++程序设计中虚基类,多继承知识点

一.前言 如上 二.题目 分别声明Teacher(教师)类和Cadre(干部)类&#xff0c;采用多重继承方式由这两个类派 生出新类Teacher_Cadre(教师兼干部&#xff09;类。要求&#xff1a; &#xff08;1)在两个基类中都包含姓名、年龄、性别、地址、电话等数据成员。 &#xff08;2&a…...

FFmpeg:avcodec_encode_video()

本文简单分析FFmpeg的avcodec_encode_video2()函数。该函数用于编码一帧视频数据。avcodec_encode_video2()函数的声明位于libavcodec\avcodec.h&#xff0c;如下所示。 /*** Encode a frame of video.** Takes input raw video data from frame and writes the next output p…...

SpringBoot中必须掌握的45个注解

1.SpringBoot/spring SpringBootApplication: 包含Configuration、EnableAutoConfiguration、ComponentScan通常用在主类上&#xff1b; Repository: 用于标注数据访问组件&#xff0c;即DAO组件&#xff1b; Service: 用于标注业务层组件&#xff1b; RestController: …...

鼻塞用鼻喷剂-调研(成分+刺激性)

称谓&#xff1a; 鼻滴水、鼻喷剂、滴鼻药 鼻喷剂原理: 鼻喷剂刺激鼻腔: 搜集标准&#xff1a; ①拼多多和淘宝上销量≥1 ②删除宠物用滴鼻药水 ③相同成分药水会有多种品牌&#xff0c;不重复列举 ④来自淘宝、 拼多多、京东&#xff0c;搜索关键词为"鼻塞 药水"…...

ESP8266|RPi pico 通过ESP8266搭建web服务器(树莓派pico通过esp8266联网)

文章目录前言软件准备硬件准备接线C/C实现代码实验现象Micropython实现代码实验现象参考资料前言 Raspberry pi pico官网介绍&#xff1a;https://www.raspberrypi.org/documentation/rp2040/getting-started/#board-specificationsESP8266官网介绍&#xff1a;https://www.es…...

CentOS系列 docker安装以及virtualenv和virtualenvwrapper的安装及使用

环境&#xff1a;CentOS7.7 以下命令若执行不成功 试着在命令开头加上sudo 1、docker # 先升级一下yum yum -y update # 设置docker的阿里源&#xff0c;如果第一步执行失败&#xff0c;就先执行下面那条&#xff0c;然后再回来执行这条&#xff1b; yum-config-manager --add…...

高分子化学顺着思路走

目录常用名&#xff08;必记&#xff09;&#xff1a;每章需要思考的内容缩聚和逐步聚合自由基聚合机理烯类单体对聚合机理的选择性引发引发剂其他的引发方式&#xff1a;热引发&#xff0c;光引发&#xff0c;辐射引发&#xff0c;等离子体引发&#xff0c;微波引发引发效率聚…...

官宣!《新程序员·开发者黄金十年》正式发布

“在互联网还不发达的时代&#xff0c;就是《程序员》伴我成长的。” “怀念啊&#xff0c;曾经《程序员》的日子。” “我是看着《程序员》长大的&#xff0c;大二时买了创刊号。” “这可是我们这一代人大学时候满满的回忆&#xff0c;也是当时寝室中传阅最多的书。” “…...

异步处理方式探索

这里写自定义目录标题最佳异步处理方式探索CompletableFutureThreadPoolTaskExecutorJDK8并行流处理最佳异步处理方式探索 JDK提供了多种异步处理方式&#xff0c;其中归结出来分为两大类。 java.lang.Runnable 通过 run()方法新建一个线程处理任务&#xff0c;无返回值java.u…...

为啥集群小文件治理那么重要,你真的懂吗?

小文件是 Hadoop 集群运维中的常见挑战&#xff0c;尤其对于大规模运行的集群来说可谓至关重要。如果处理不好&#xff0c;可能会导致许多并发症。Hadoop集群本质是为了TB,PB规模的数据存储和计算因运而生的。为啥大数据开发都说小文件的治理重要&#xff0c;说HDFS 存储小文件…...

09.流程控制

09.流程控制 1.引子 流程控制即控制流程&#xff0c;具体指控制程序的执行流程&#xff0c;而程序的执行流程分为三种结构&#xff1a;顺序结构&#xff08;之前我们写的代码都是顺序结构&#xff09;、分支结构&#xff08;用到if判断&#xff09;、循环结构&#xff08;用到…...

Trace

TRACE:在命令行上捕获系统跟踪记录https://developer.android.google.cn/topic/performance/tracing/command-line?hlzh-cn perfetto https://developer.android.google.cn/studio/command-line/perfetto?hlzh-cn 开发这模式的系统trace使用PerfettoUI打开https://develope…...

HTML列表标签和表格标签

目录 一、列表标签与表格标签要点 二、列表 LIST &#xff08;一&#xff09;有序列表: &#xff08;二&#xff09;无序列表: &#xff08;三&#xff09;自定义&#xff08;相互嵌套使用&#xff09; 三、表格标签 TABLE &#xff08;一&#xff09;表格标签 (二&…...

搭建集群常用脚本

rsync同步工具&#xff0c;编写xsync脚本 yum install -y rsynchostname处理&#xff1a; vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomai…...

qt线程 qthread(转)

qt线程&#xff08;转&#xff09;----这篇很专业&#xff01; 2010-02-19 16:02:19标签&#xff1a;qt   [推送到技术圈]本文档是自己所整理的一份文档&#xff0c;部分是原创&#xff0c;还转贴了网上的一此资料(已经标明了),(难点是多线程的编写)&#xff0c;是有源代码的…...

基于Linux的视频传输系统(转)

目录 1 原创性声明----------------------------------------------------3 2 摘要----------------------------------------------------------4 3 系统方案------------------------------------------------------4 3.1 功能与指标--------------…...

基于TMS320DM355的VGA视频采集系统的设计与实现

基于TMS320DM355的VGA视频采集系统的设计与实现 【摘要】本文提出了以TMS320DM355为核心&#xff0c;对VGA信号进行实时采集及显示的系统方案。系统采用AD9883芯片将VGA信号数字化&#xff0c;利用FPGA芯片进行时序转换&#xff0c;修改了DM355的视频处理前端的驱动&#xff0c…...

JRTPLIB@Conference(转)

JRTPLIBConference&#xff08;转&#xff09; 2011-03-28 14:56:41| 分类&#xff1a; 流媒体 | 标签&#xff1a; |字号大中小 订阅 本文转自&#xff1a;http://www.cnitblog.com/tinnal/archive/2009/01/01/53342.html JRTPLIBConference DIY视频会议系统 一、开编 …...

opencv超好学习资料

日志 返回日志列表 opencv超好学习资料 2010-9-25 13:40阅读(0)赞赞赞赞转载分享评论复制地址举报编辑 上一篇 |下一篇&#xff1a;opencv安装方法 w...v首页 资讯 研发 移动 云计算 空间 学生 论坛 博客 下载 网摘 程序员 外包 书店 CTO俱乐部 TUP 培训充电 D9区 xsoul007的专…...

网搜源码

VideoNet.rar - 该程序可以用于两个人在LAN/Intranet(或者Internet)上进行视频会议。现在有许多视频会议程序&#xff0c;每个都有各自的性能提升技术。主要的问题是视频会议视频帧的尺寸对于传输来说太大。因此&#xff0c;性能依赖于对帧的编解码。 , This procedure may use…...

基于Linux的视频传输系统

目录 1原创性声明----------------------------------------------------3 2 摘要----------------------------------------------------------4 3系统方案------------------------------------------------------4 3.1功能与指标--------------------------------------…...

基于Linux的视频传输系统(上大学时參加的一个大赛的论文)

文件夹<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />1原创性声明----------------------------------------------------32 摘要----------------------------------------------------------43系统方案---------------------…...

网搜

VideoNet.rar - 该程序可以用于两个人在LAN/Intranet(或者Internet)上进行视频会议。现在有许多视频会议程序&#xff0c;每个都有各自的性能提升技术。主要的问题是视频会议视频帧的尺寸对于传输来说太大。因此&#xff0c;性能依赖于对帧的编解码。 , This procedure may use…...

Android中常见的数据加密和编码方式

要点1&#xff1a;MD5算法&#xff08;单向加密&#xff09; MD5即Message-Digest Algorithm 5&#xff08;信息-摘要算法5&#xff09;&#xff0c;用于确保信息传输完整一致&#xff0c;是计算机广泛使用的杂凑算法之一&#xff08;又译摘要算法、哈希算法&#xff09;。MD5…...

linux网络传输协议之TCP/IP

1 网络通信的起源与层次 1.1 起源 &#xff08;1&#xff09;为了满足两个不同主机&#xff08;包括能联网的linux设备、PC机等&#xff09;进程间通信&#xff0c;这个时候就需要用网络&#xff08;有线或无线&#xff09; 2.1 层次&#xff08;应用层->操作系统API-…...

Android 恢复出厂设置时间重置

时间由rtc硬件模块来进行维护的&#xff0c;时间更新后会将时间信息写入此硬件模块&#xff0c;在系统启动时&#xff0c;RTC硬件驱动会读取此值进行设置。 关于android的时间 网上资料了解到&#xff0c;android Linux都有两个时间&#xff0c;一个是系统时间&#xff0c;一个…...

jrtplib库的使用

jrtplib库的使用 收藏 一、sample是一个简单的IPv4的列子&#xff0c;它实现了RTP在本机上的数据的传输。 1、初始化。 我们知道RTP是通常是使用UDP协议来实现数据的传输&#xff0c;在windows环境下&#xff0c;当然要用到我们熟悉的套接字的使用&#xff0c;所以我们先要进…...

初识容器集群管理系统 Kubernetes

什么是 Kubernetes Kubernetes 是 Google 2014 年创建管理的&#xff0c;是 Google 10 多年大规模容器管理技术 Borg 的开源版本。 Kubernetes 是容器集群管理系统&#xff0c;是一个开源的平台&#xff0c;可以实现容器集群的自动化部署、自动扩缩容、维护等功能。使用 Kuber…...

关于单元测试框架GoogleTest——参考《百度文库》、大量博客

本文章集齐百家之所长&#xff08;又臭又长&#xff09;&#xff0c;读者可根据自己的需求自取&#xff1a;一、关于测试1.1 为啥要测试&#xff1f;1.2 测试的分类1.3 [单元测试-参考web[song]的博客]1.3.1、为什么单元测试&#xff1f;1.3.2、[传统方法单元测试-参考魏波的资…...

38款 流媒体服务器开源软件

Flash流媒体服务器Red5 Red5是一个采用Java开发开源的Flash流媒体服务器。它支持&#xff1a;把音频&#xff08;MP3&#xff09;和视频&#xff08;FLV&#xff09;转换成播放流&#xff1b; 录制客户端播放流&#xff08;只支持FLV&#xff09;&#xff1b;共享对象&#xff…...

led柔性屏是什么

led柔性屏也叫led软屏&#xff0c;是由led软模组做成的&#xff0c;led软模组是制作led显示屏的一种&#xff0c;采用了柔性设计。那led柔性有是什么特点呢? led柔性屏产品特点&#xff1a; 1、模组柔软&#xff0c;型号选择独特&#xff0c;安装方便&#xff1b; 2、面罩由…...

汽车需要柔性前挡显示屏

想像一下&#xff0c;你的汽车前挡风玻璃是一整块的柔性屏(权且称它为前挡屏)&#xff0c;它看起来和现在的玻璃一样&#xff0c;不过实际上是通过车顶的高清摄像头传回的视频&#xff0c;通过真正的车载电脑加工以后&#xff0c;叠加相关信息&#xff0c;同时显示在屏幕上的。…...

BOE(京东方)柔性屏显示技术发展势头强劲,赋能应用场景创新

在7月14日CCTV1播出的《生活圈》节目里有这样一场实验&#xff1a;一块比白纸还要薄的显示屏在被一辆轿车两次碾压过后仍保持完好无损&#xff0c;显示一切正常。白纸经过卷曲后会留下痕迹、边缘翘起&#xff0c;无法再次平整展开&#xff0c;而比纸更轻薄的屏幕随意弯折却不留…...