【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01—短信/邮件/异常/MD5


持续学习&持续更新中…

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01

  • 环境搭建
  • 验证码倒计时
  • 短信服务
  • 邮件服务
  • 验证码
    • 短信形式:
    • 邮件形式:
  • 异常机制
  • MD5
  • 参考

环境搭建

C:\Windows\System32\drivers\etc\hosts

192.168.56.10 gulimall.com
192.168.56.10 search.gulimall.com
192.168.56.10 item.gulimall.com
192.168.56.10 auth.gulimall.com

Nginx配置:(记得使用Nginx动静分离)

在这里插入图片描述

# ...http {# ...upstream gulimall {server 192.168.193.107:88;}include /etc/nginx/conf.d/*.conf;
}

网关:

        - id: gulimall_auth_routeuri: lb://gulimall-authpredicates:- Host=auth.gulimall.com

gulimall-auth:

@Controller
public class LoginController {@GetMapping("/login.html")public String loginPage() {return "login";}@GetMapping("/reg.html")public String regPage() {return "reg";}
}

或者:

@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {/*** 视图映射*/@Overridepublic void addViewControllers(ViewControllerRegistry registry) {/***     @GetMapping("/login.html")*     public String loginPage(){*          //空方法*         return "login";*     }*///只是get请求能映射registry.addViewController("/login.html").setViewName("login");registry.addViewController("/reg.html").setViewName("reg");}
}

验证码倒计时

前端:

在这里插入图片描述

    $(function () {$("#sendCode").click(function () {//2、倒计时if ($(this).hasClass("disabled")) {//正在倒计时。} else {//1、给指定手机号发送验证码// $.get("/sms/sendEmail?email=" + $("#phoneNum").val(), function (data) {$.get("/sms/sendcode?phone=" + $("#phoneNum").val(), function (data) {if (data.code != 0) {alert(data.msg);}});timeoutChangeStyle();}});})var num = 60;function timeoutChangeStyle() {$("#sendCode").attr("class", "disabled");if (num == 0) {$("#sendCode").text("发送验证码");num = 60;$("#sendCode").attr("class", "");} else {var str = num + "s 后再次发送";$("#sendCode").text(str);setTimeout("timeoutChangeStyle()", 1000);}num--;}

短信服务

购买短信套餐后,扫码激活,然后绑定测试手机号码:

在这里插入图片描述

然后点击:调用API发送短信 按钮 (使用【专用】测试签名/模板)

在这里插入图片描述

然后 发起调用 ,复制相关信息即可

在这里插入图片描述

增加权限授予RAM子账号SMS和MPush的权限。

在这里插入图片描述

        <dependency><groupId>com.aliyun</groupId><artifactId>alibabacloud-dysmsapi20170525</artifactId><version>3.0.0</version></dependency>
// This file is auto-generated, don't edit it. Thanks.
package com.atguigu.gulimall.auth.sms;import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.sdk.service.dysmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.sdk.service.dysmsapi20170525.models.SendSmsResponse;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;import java.util.concurrent.CompletableFuture;public class SendSms {public static void main(String[] args) throws Exception {// HttpClient Configuration/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder().connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds.maxConnections(128) // Set the connection pool size.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds// Configure the proxy.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001)).setCredentials("<your-proxy-username>", "<your-proxy-password>"))// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true)).x509TrustManagers(new X509TrustManager[]{}).keyManagers(new KeyManager[]{}).ignoreSSL(false).build();*/// Configure Credentials authentication information, including ak, secret, tokenStaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()// Please ensure that the environment variables ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET are set..accessKeyId("xxxx").accessKeySecret("xxxx")//.securityToken(System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")) // use STS token.build());// Configure the ClientAsyncClient client = AsyncClient.builder().region("cn-shanghai") // Region ID//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient).credentialsProvider(provider)//.serviceConfiguration(Configuration.create()) // Service-level configuration// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc..overrideConfiguration(ClientOverrideConfiguration.create()// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi.setEndpointOverride("dysmsapi.aliyuncs.com")//.setConnectTimeout(Duration.ofSeconds(30))).build();// Parameter settings for API requestSendSmsRequest sendSmsRequest = SendSmsRequest.builder().signName("阿里云短信测试").templateCode("xxxx").phoneNumbers("xxxx").templateParam("{\"code\":\"1111\"}")// Request-level configuration rewrite, can set Http request parameters, etc.// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders())).build();// Asynchronously get the return value of the API requestCompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);// Synchronously get the return value of the API requestSendSmsResponse resp = response.get();System.out.println(new Gson().toJson(resp));// Asynchronous processing of return values/*response.thenAccept(resp -> {System.out.println(new Gson().toJson(resp));}).exceptionally(throwable -> { // Handling exceptionsSystem.out.println(throwable.getMessage());return null;});*/// Finally, close the clientclient.close();}}

简单把这些代码整改一下:

@Configuration
public class SMSConfig {@Value("${spring.cloud.alicloud.access-key}")private String accessId;@Value("${spring.cloud.alicloud.secret-key}")private String secretKey;@Beanpublic StaticCredentialProvider provider() {return StaticCredentialProvider.create(Credential.builder().accessKeyId(accessId).accessKeySecret(secretKey).build());}}
@RestController
public class SendSmsController {@Autowiredprivate StaticCredentialProvider provider;/*** 提供接口,供别的服务调用** @param phone* @param code* @return "body": {* "bizId": "774515119736291045^0",* "code": "OK",* "message": "OK",* "requestId": "D6BD5A90-8755-5C82-B631-0F40AB7B41B0"* }*/@GetMapping("/sms/send")public R sendSms(@RequestParam("phone") String phone, @RequestParam("code") String code) throws ExecutionException, InterruptedException {AsyncClient client = AsyncClient.builder().region("cn-shanghai") // Region ID.credentialsProvider(provider).overrideConfiguration(ClientOverrideConfiguration.create().setEndpointOverride("dysmsapi.aliyuncs.com")).build();SendSmsRequest sendSmsRequest = SendSmsRequest.builder().signName("阿里云短信测试").templateCode("SMS_154950909").phoneNumbers(phone).templateParam("{\"code\":\"" + code + "\"}").build();CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);SendSmsResponse resp = response.get();/*{"headers": {"Keep-Alive": "timeout\u003d25" ......},"statusCode": 200,"body": {"bizId": "774515119736291045^0","code": "OK","message": "OK","requestId": "D6BD5A90-8755-5C82-B631-0F40AB7B41B0"}}*/client.close();if (resp.getBody().getMessage().equalsIgnoreCase("OK")) return R.ok();return R.error(BizCodeEnume.SMS_SEND_EXCEPTION);}}

邮件服务

<dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.1</version>
</dependency>
@Data
public class EmailVo {private String receiveMail;private String subject;private String content;
}
@Configuration
public class EmailConfig {// 我在Nacos配置中心配的user和password@Value("${mail.user}")private String mailUser;@Value("${mail.password}")private String mailPassword;@Beanpublic Properties props() {// 创建Properties 类用于记录邮箱的一些属性Properties props = new Properties();// 表示SMTP发送邮件,必须进行身份验证props.put("mail.smtp.auth", "true");//此处填写SMTP服务器props.put("mail.smtp.host", "smtp.qq.com");//端口号,QQ邮箱端口587props.put("mail.smtp.port", "587");// 此处填写,写信人的账号props.put("mail.user", mailUser);// 此处填写16位STMP口令props.put("mail.password", mailPassword);return props;}@Beanpublic Authenticator authenticator(Properties props) {// 构建授权信息,用于进行SMTP进行身份验证return new Authenticator() {protected PasswordAuthentication getPasswordAuthentication() {// 用户名、密码String userName = props.getProperty("mail.user");String password = props.getProperty("mail.password");return new PasswordAuthentication(userName, password);}};}
}
@RestController
public class SendEmailController {@Autowiredprivate Properties props;@Autowiredprivate Authenticator authenticator;@PostMapping("/email/send")public R sendEmail(@RequestBody EmailTo emailTo) throws MessagingException {// 使用环境属性和授权信息,创建邮件会话Session mailSession = Session.getInstance(props, authenticator);// 创建邮件消息MimeMessage message = new MimeMessage(mailSession);// 设置发件人InternetAddress form = new InternetAddress(props.getProperty("mail.user"));message.setFrom(form);// 设置收件人的邮箱InternetAddress to = new InternetAddress(emailTo.getReceiveMail());message.setRecipient(Message.RecipientType.TO, to);// 设置邮件标题message.setSubject(emailTo.getSubject());// 设置邮件的内容体message.setContent(emailTo.getContent(), "text/html;charset=UTF-8");// 最后当然就是发送邮件啦Transport.send(message);return R.ok();}}

在这里插入图片描述

验证码

短信形式:

    @GetMapping("/sms/sendcode")public R sendCode(@RequestParam("phone") String phone) {
//        Redis缓存验证码:存起来方便下次校验 以及 可以给验证码设置有效期String code = getRandomCode().toString();//        防止同一个手机号在60s内再次发送验证码String key = AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone;String oldCode = stringRedisTemplate.opsForValue().get(key);if (!StringUtils.isEmpty(oldCode)) {long l = Long.parseLong(oldCode.split("_")[1]);if (System.currentTimeMillis() - l < 60000) { // 如果时间间隔小于60sreturn R.error(BizCodeEnume.SMS_MULTI_EXCEPTION);}}//        R r = thirdPartyFeignService.sendSms(phone, code);
//        if (r.getCode() == BizCodeEnume.SUCCESS.getCode()) {
//            code = code + "_" + System.currentTimeMillis();
//            stringRedisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES); //过期时间5分钟
//        }
//        return r;CompletableFuture.runAsync(() -> thirdPartyFeignService.sendSms(phone, code), threadPool);CompletableFuture.runAsync(() -> {stringRedisTemplate.opsForValue().set(key, codeResolve(code), 5, TimeUnit.MINUTES); //过期时间5分钟}, threadPool);return R.ok();}

生成验证码(随机四位数):

    private Integer getRandomCode() {//4位数字验证码:想要[1000,9999],也就是[1000,10000)// Math.random() -> [0, 1)  // (int) Math.random()永远为0// Math.random() * (end - begin) -> [0, end - begin)// begin + Math.random() * (end - begin) -> [begin, end)int code = (int) (1000 + Math.random() * (10000 - 1000));return code;}

邮件形式:

    @GetMapping("/sms/sendEmail")public R sendEmailCode(@RequestParam("email") String email) throws MessagingException {String code = UUID.randomUUID().toString().substring(0, 5);String key = AuthServerConstant.EMAIL_CODE_CACHE_PREFIX + email;String oldCode = stringRedisTemplate.opsForValue().get(key);if (!StringUtils.isEmpty(oldCode)) { // 说明5分钟内已经给该邮箱发送过验证码了long l = Long.parseLong(oldCode.split("_")[1]);if (System.currentTimeMillis() - l < 60000) { // 如果时间间隔小于60sreturn R.error(BizCodeEnume.SMS_MULTI_EXCEPTION);}}CompletableFuture.runAsync(() -> {// 给Redis放置验证码String realSaveCode = code + "_" + System.currentTimeMillis();stringRedisTemplate.opsForValue().set(key, realSaveCode, 5, TimeUnit.MINUTES); //过期时间5分钟}, threadPool);CompletableFuture.runAsync(() -> {// 发送邮件try {EmailTo emailTo = new EmailTo();emailTo.setReceiveMail(email);emailTo.setContent("验证码:" + code + "——有效期5分钟!");emailTo.setSubject("欢迎注册!");thirdPartyFeignService.sendEmail(emailTo);} catch (MessagingException e) {e.printStackTrace();}}, threadPool);return R.ok();}

异常机制

    @PostMapping("/regist")public R regist(@RequestBody MemberRegistVo vo){try{memberService.regist(vo);}catch (PhoneExistException e){return R.error(BizCodeEnume.PHONE_EXIST_EXCEPTION);}catch (UsernameExistException e){return R.error(BizCodeEnume.USER_EXIST_EXCEPTION);}return R.ok();}
    @Overridepublic void regist(MemberRegistVo vo) {//检查用户名和手机号是否唯一。为了让controller能感知异常:异常机制String phone = vo.getPhone(); checkPhoneUnique(phone);String userName = vo.getUserName(); checkUsernameUnique(userName);MemberEntity entity = new MemberEntity();entity.setMobile(phone);entity.setUsername(userName);entity.setNickname(userName);//设置默认等级MemberLevelEntity levelEntity = memberLevelDao.getDefaultLevel();entity.setLevelId(levelEntity.getId());//密码要进行加密存储。//当然,也可以在前端就加密发过来BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String encode = passwordEncoder.encode(vo.getPassword());entity.setPassword(encode);//其他的默认信息//保存this.baseMapper.insert(entity);}
    @Overridepublic void checkPhoneUnique(String phone) throws PhoneExistException {Integer mobile = this.baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));if (mobile > 0) {throw new PhoneExistException();}}@Overridepublic void checkUsernameUnique(String username) throws UsernameExistException {Integer count = this.baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", username));if (count > 0) {throw new UsernameExistException();}}
public class UsernameExistException extends RuntimeException {public UsernameExistException() {super("用户名存在");}
}

R:

public class R extends HashMap<String, Object> {public static final String CODE = "code";public static final String MSG = "msg";public static final String DATA = "data";//利用fastjson进行逆转public <T> T getData(String key, TypeReference<T> typeReference) {Object data = get(key);// 默认是mapString s = JSON.toJSONString(data); // 得转为JSON字符串T t = JSON.parseObject(s, typeReference);return t;}//利用fastjson进行逆转public <T> T getData(TypeReference<T> typeReference) {return getData(DATA, typeReference);}public R setData(Object data) {put(DATA, data);return this;}public R() {put(CODE, BizCodeEnume.SUCCESS.getCode());put(MSG, BizCodeEnume.SUCCESS.getMsg());}public static R error() {return error("服务器未知异常,请联系管理员");}public static R error(String msg) {
//        500return error(org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error(int code, String msg) {R r = new R();r.put(CODE, code);r.put(MSG, msg);return r;}public static R error(BizCodeEnume bizCodeEnume) {R r = new R();r.put(CODE, bizCodeEnume.getCode());r.put(MSG, bizCodeEnume.getMsg());return r;}public static R ok(String msg) {R r = new R();r.put(MSG, msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;}public Integer getCode() {return (Integer) this.get(CODE);}public String getMsg() {return (String) this.get(MSG);}
}
/**** TODO 写博客* 错误码和错误信息定义类* 1. 错误码定义规则为5位数字* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。*      10:通用             000:系统未知异常* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式* 错误码列表:*  10: 通用*      001:参数格式校验*  11: 商品*  12: 订单*  13: 购物车*  14: 物流*/
public enum BizCodeEnume {SUCCESS(0, "OK"),HTTP_SUCCESS(200, "OK"),UNKNOW_EXCEPTION(10000,"系统未知异常"),VAILD_EXCEPTION(10001,"参数格式校验失败"),TOO_MANY_REQUEST(10002,"请求流量过大"),SMS_MULTI_EXCEPTION(10003,"验证码获取频率太高,请1分钟后再试"),SMS_SEND_EXCEPTION(10004,"验证码发送失败"),SMS_CODE_EXCEPTION(10005,"验证码错误"),REG_ERROR_EXCEPTION(10006,"用户名或手机已存在,注册失败"),PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),USER_EXIST_EXCEPTION(15001,"用户存在"),PHONE_EXIST_EXCEPTION(15002,"手机号存在"),NO_STOCK_EXCEPTION(21000,"商品库存不足"),LOGINACCT_PASSWORD_INVAILD_EXCEPTION(15003,"账号密码错误");private final int code;private final String msg;BizCodeEnume(int code,String msg){this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}
}

MD5

MD5:Message Digest algorithm 5,信息摘要算法

  • 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
  • 容易计算:从原数据计算出MD5值很容易。
  • 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
  • 强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
  • 不可逆(即使知道加密算法,也不能反推出明文密码): MD5是一种信息摘要算法,会损 失元数据,所以不可逆出原数据是什么

但是,由于MD5的抗修改性和强抗碰撞(一个字符串的MD5值永远是那个值),发明了彩虹表(暴力 破解)。所以,MD5不能直接进行密码的加密存储

加盐:

  • 通过生成随机数与MD5生成字符串进行组合
  • 数据库同时存储MD5值与salt值。验证正确性时使用salt进行MD5即可
百度网盘的秒传:在上传文件之前,计算出该文件的MD5值,看有没有人之前上传过,也就是去匹配百度网盘的数据库中有没有相同的 MD5 值, 如果有一样的就不用传了 
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallAuthApplicationTests {@Testpublic void contextLoads() {//MD5是不可逆的,但是利用它的抗修改性(一个字符串的MD5值永远是那个值),发明了彩虹表(暴力破解)。//所以,MD5不能直接进行密码的加密存储;
//        String s = DigestUtils.md5Hex("123456");//盐值加密;随机值 加盐 :$1$ + 8位字符
//        只要是同一个材料,做出来的饭是一样的,如果给饭里随机撒点“盐”,那么,饭的口味就不一样了//"123456"+System.currentTimeMillis();//想要再次验证密码咋办?: 将密码再进行盐值(去数据库查当时保存的随机盐)加密一次,然后再去匹配密码是否正确
//        String s1 = Md5Crypt.md5Crypt("123456".getBytes()); //随机盐
//        String s1 = Md5Crypt.md5Crypt("123456".getBytes(),"$1$qqqqqqqq"); //指定盐
//        System.out.println(s1);//        给数据库加字段有点麻烦,Spring有好用的工具:BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//        String encode = passwordEncoder.encode("123456");
//        $2a$10$coLmFyeppkTPTfD0RJgqL.nx33s0wvUmj.shqEM/6hvwOO4TWiGmy
//        $2a$10$4IP4F/2iFO2gbSvQKyJzGuI3RhU5Qdtr519KsyoXGAy.b7WT4P1RW
//        $2a$10$0hEI3vMkTbTqK76990MGu.s9QKrkjDSpgyhfzR4zsy07oKB9Jw.PS//        System.out.println(encode);
//        boolean matches = passwordEncoder.matches("123456", "$2a$10$0hEI3vMkTbTqK76990MGu.s9QKrkjDSpgyhfzR4zsy07oKB9Jw.PS");boolean matches = passwordEncoder.matches("lpruoyu123", "$2a$10$m7TmOQAin5Tj6QzV1TT0ceW6iLypdN8LHkYP16DUEngJUfYNgWVEm");System.out.println(matches);}
}

在这里插入图片描述

在这里插入图片描述

参考

雷丰阳: Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目.


本文完,感谢您的关注支持!


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

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

相关文章

使用flask的web网页部署介绍

使用flask的web网页部署介绍 文章目录 前言一、网页介绍二、数据库设计介绍总结 前言 flaskbootstrapjquerymysql搭建三叶青在线识别网站&#xff0c;使用nginxgunicorn将网站部署在腾讯云上&#xff0c;配置SSL证书。网站地址&#xff1a;https://www.whtuu.cn 三叶青图像识…

2024年6月后2周重要的大语言模型论文总结:LLM进展、微调、推理和对齐

本文总结了2024年6月后两周发表的一些最重要的大语言模型论文。这些论文涵盖了塑造下一代语言模型的各种主题&#xff0c;从模型优化和缩放到推理、基准测试和增强性能。 LLM进展与基准 1、 BigCodeBench: Benchmarking Code Generation with Diverse Function Calls and Com…

【C++】模板进阶--保姆级解析(什么是非类型模板参数?什么是模板的特化?模板的特化如何应用?)

目录 一、前言 二、什么是C模板&#xff1f; &#x1f4a6;泛型编程的思想 &#x1f4a6;C模板的分类 三、非类型模板参数 ⚡问题引入⚡ ⚡非类型模板参数的使用⚡ &#x1f525;非类型模板参数的定义 &#x1f525;非类型模板参数的两种类型 &#x1f52…

linux下高级IO模型

高级IO 1.高级IO模型基本概念1.1 阻塞IO1.2 非阻塞IO1.3 信号驱动IO1.4 IO多路转接1.5 异步IO 2. 模型代码实现2.1 非阻塞IO2.2 多路转接-selectselect函数介绍什么才叫就绪呢&#xff1f;demoselect特点 2.3 多路转接-pollpoll函数介绍poll优缺点demo 2.4 多路转接-epoll&…

为什么人一旦开窍了就变的特别厉害?

点击上方△腾阳 关注 《让子弹飞》这部电影非常经典&#xff0c;其中一个名场面就是“六子吃粉”。 电影里&#xff0c;胡万对着老六就是一顿狂轰滥炸&#xff1a;“吃了两碗粉&#xff0c;就给一碗的钱&#xff0c;你当咱这是慈善堂呢&#xff1f;” 老六一听&#xff0c;那…

SpringBoot+ELK 收集日志的两种方式

方式一、FileBeatlogstash 7.5.1(docker)ES(docker)springboot 日志文件 应用方式 我们采用ELFK 架构采集日志&#xff0c;直接读取日志生成的文件&#xff0c;不对Springboot的日志任何的修改。也就是FileBeat 通过读取日志文件位置获取日志内容&#xff0c;然后发送至logsta…

移动应用开发课设——原神小助手文档(1)

2023年末&#xff0c;做的移动应用开发课设&#xff0c;分还算高&#xff0c;项目地址&#xff1a;有帮助的话&#xff0c;点个赞和星呗~ GitHub - blhqwjs/-GenShin_imp: 2023年移动应用开发课设 本文按照毕业论文要求来写&#xff0c;希望对大家有所帮助。 xxxx大学课程设计报…

C++--partition库函数

介绍 在C中&#xff0c;partition函数通常是指STL&#xff08;Standard Template Library&#xff09;中的std::partition算法&#xff0c;它用于对一个序列进行分区操作。具体来说&#xff0c;std::partition接受一个范围和一个谓词&#xff08;predicate&#xff09;作为参数…

win10使用小技巧一

1. 查看电脑IP地址 步骤&#xff1a;按WinR打开运行框 → 输入cmd点确定 → 输入ipconfig回车 → 查看IP地址。 2. 解决网页文字不能复制 步骤&#xff1a;按F12 → 调试框里点击设置 → 向下滑找到 禁用 JavaScript → 勾选 → 复制文字。 3. 解决电脑不能上网 方法一&…

im即时通讯哪家好?WorkPlus im即时通讯集成底座为企业保驾护航

在当今数字化时代&#xff0c;即时通讯是企业内部沟通和协作的重要工具&#xff0c;提高工作效率和团队协作效果。在众多IM即时通讯提供商中&#xff0c;WorkPlus作为一家具有独特优势的企业IM即时通讯集成底座&#xff0c;为企业提供了全面的功能和安全保障&#xff0c;为企业…

Linux权限概述

一、权限概述 1.权限的基本概念 2.为什么要设置权限 3.linux用户的身份类别 4.user文件的拥有者 5.group文件所属组内用户 6.other其他用户 7.特殊用户root 二、普通权限管理 1.ls -l查看文件权限 2.文件类型以及权限解析 3.文件或文件夹的权限设置 4.通过数字给文件…

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.3-2.4

目录 第三门课 结构化机器学习项目&#xff08;Structuring Machine Learning Projects&#xff09;第二周&#xff1a;机器学习策略&#xff08;2&#xff09;(ML Strategy (2))2.3 快速搭建你的第一个系统&#xff0c;并进行迭代&#xff08;Build your first system quickly…

Python 学习中什么是元组,如何使用元组?

什么是元组 元组&#xff08;Tuple&#xff09;是Python内置的一种数据结构&#xff0c;用于存储多个数据项。与列表类似&#xff0c;元组也可以存储不同类型的数据&#xff0c;但它们之间存在一个重要区别&#xff1a;元组是不可变的&#xff0c;也就是说&#xff0c;一旦创建…

智慧校园综合解决方案PPT(41页)

1. 方案背景 智慧校园综合解决方案响应《教育信息化2.0行动计划》等政策&#xff0c;旨在加快智慧校园建设&#xff0c;推动信息化与学习生活的深度融合。目前教育信息化配套设施建设存在“孤岛架构”&#xff0c;学生安全问题频发&#xff0c;技术发展迅速&#xff0c;家长对…

专题三:Spring源码中新建module

前面我们构建好了Spring源码&#xff0c;接下来肯定迫不及待来调试啦&#xff0c;来一起看看大名鼎鼎ApplicationContext 新建模块 1、基础步骤 1.1 自定义模块名称如&#xff1a;spring-self 1.2 选择构建工具因为spring使用的是gradle&#xff0c;所以这边需要我们切换默认…

Android 如何通过代码实时设置EditTextView光标

背景&#xff1a;换肤框架下&#xff0c;QA进行深色浅色切换说输入框光标颜色没有改变&#xff0c;转UI结果UI说需要修改&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 本来有方法可以设置&#xff0c;但是 设置后未生效。重新进入该页面才生效&#xff01;&a…

Android 集成OpenCV

记录自己在学习使用OpenCV的过程 我使用的是4.10.0 版本 Android 集成OpenCV 步骤 下载OpenCV新建工程依赖OpenCV初始化及逻辑处理 1、下载OpenCV 并解压到自己的电脑 官网 地址&#xff1a;https://opencv.org/releases/ 个人地址&#xff1a;https://pan.baidu.com/s/19f…

这款新的 AI 语音助手击败了 OpenAI,成为 ChatGPT 最受期待的功能之一

OpenAI 推迟了 ChatGPT 令人印象深刻的语音模式&#xff0c;这让许多 AI 聊天机器人的粉丝感到不安&#xff0c;但他们现在可能已经被挖走了。法国人工智能开发商 Kyutai 推出了一款名为 Moshi 的实时语音 AI 助手。 Moshi 旨在通过语音&#xff08;如 Alexa 或 Google Assista…

三、数据库系统(考点篇)试题

聚簇索引&#xff0c;也叫簇类索引&#xff0c;原理是对磁盘上实际数据重新组织以按指定的一个或多个列的值排序于聚簇索引的索引页面指针指向数据页面&#xff0c;所以使用聚簇索引查找数据几乎总是比使用非聚簇索引快。每张表只能建一个聚簇索引&#xff0c;并且建聚簇索引需…

在VMware中安装Linux RHEL8操作系统

Linux操作系统安装 任务目标 了解虚拟机平台VMWARE的安装步骤。 了解RHEL8的安装步骤。 熟悉安装所必须的硬件环境。 任务要求 在VMWARE虚拟机平台上安装RHEL8&#xff0c;要求使用root用户成功登录&#xff0c;关闭虚拟机做好快照。将安装步骤记录在下方“操作步骤”&am…