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


持续学习&持续更新中…

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【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/web/39032.shtml

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

相关文章

JAVA每日作业day7.1-7.3小总结

ok了家人们前几天学了一些知识&#xff0c;接下来一起看看吧 一.API Java 的 API &#xff08; API: Application( 应用 ) Programming( 程序 ) Interface(接口 ) &#xff09; Java API 就是 JDK 中提供给我们使用的类&#xff0c;这些类将底层 的代码实现封装了起来&#x…

【echarts】拖拽滑块dataZoom-slider自定义样式,简单适配移动端

电脑端 移动端 代码片段 dataZoom: [{type: inside,start: 0,end: 100},{type: slider,backgroundColor: #F2F5F9,fillerColor: #BFCCE3,height: 13, // 设置slider的高度为15start: 0,end: 100,right: 60,left: 60,bottom: 15,handleIcon:path://M30.9,53.2C16.8,53.2,5.3,41.…

Linux源码阅读笔记12-RCU案例分析

在之前的文章中我们已经了解了RCU机制的原理和Linux的内核源码&#xff0c;这里我们要根据RCU机制写一个demo来展示他应该如何使用。 RCU机制的原理 RCU&#xff08;全称为Read-Copy-Update&#xff09;,它记录所有指向共享数据的指针的使用者&#xff0c;当要修改构想数据时&…

不要把面子太当回事

新手拍短视频真人出镜&#xff0c;会觉得拍视频不自然怎么办&#xff1f;感觉自己好傻。 其实不要把面子太当回事&#xff0c;坚持不把面子太当回事&#xff0c;反正刚开始也没人看。这是真的事实&#xff0c;大家都非常忙&#xff0c;在你身上停留的时间就几秒钟。不要在脑海…

systemctl命令使用

systemctl 作用&#xff1a;可以控制软件&#xff08;服务&#xff09;的启动、关闭、开机自启动 系统内置服务均可被systemctl控制第三方软件&#xff0c;如果自动注册了可以被systemctl控制第三方软件&#xff0c;如果没有自动注册&#xff0c;可以手动注册 语法 systemct…

企业出海如何应对国际差旅报销的复杂性?

在全球化浪潮的推动下&#xff0c;越来越多的中国企业开始迈向国际市场。然而&#xff0c;企业在“走出去”的过程中不仅面临新的商机&#xff0c;也需要克服诸多挑战。尤其是国际差旅报销的复杂性&#xff0c;成为出海企业必须解决的重要问题。 国际差旅报销的四大挑战 多元…

【分布式数据仓库Hive】常见问题及解决办法

目录 一、启动hive时发现log4j版本和hadoop的版本有冲突 解决办法&#xff1a;删除hive下高版本的slf4j 二、启动hive报错 Exception in thread "main" java.lang.NoSuchMethodError:com.google.common.base.Preconditions.checkArgument(ZLjava/lang/Object;)V …

postgres数据库的流复制

1. 流复制和逻辑复制的差异 逻辑复制和流复制最直观的不同是&#xff0c;逻辑复制支持表级别复制区分点事原理不同 逻辑日志是在wal日志产生的数据库上&#xff0c;由逻辑解析模块对wal日志进行初步的解析&#xff0c;解析结果是ReorderBufferChange&#xff08;理解为HeapTup…

干货分享|如何将前端代理服务器(BFF)接入身份认证(3完结篇)

续集3 前篇文章在前面发布&#xff0c;同学们可以自行找一下。 本篇文章将继续通过实例来详细讲解如何将前端代理服务器&#xff08;BFF&#xff09;接入身份认证。我们将使用一个示例应用来演示 BFF 与身份认证的集成过程。 3 在 Full BFF 中接入认证平台 本小节将介绍如何…

Raylib 坐标系适应与GPU绘制参数

通过750 - 鼠标坐标&#xff0c;把原点在左上角的鼠标坐标变成左下角 实现输入数据后的坐标系同GPU原点在左下角坐标相同&#xff0c; 比数组0&#xff0c;0对应左上角好&#xff0c; 此时实际上数组0&#xff0c;0对应左下角 #include <raylib.h> // 感受&#xff1a…

【SpringBoot配置文件读取】无法读取yaml文件中文字符

1. yaml配置文件 注意要将该文件编码格式改为UTF-8 spring:application:name: 好好学习admin:name: 李斯age: 24books:- name: 数据结构desc: 数据书- name: 编译原理desc: 编译书2.配置实体类 Data设置get&#xff0c;set方法Component注册为BeanConfigurationProperties(p…

开发者聊科学作息时间表

非常有幸对科学作息时间表app的开发者做一次采访。 问&#xff1a;你对科学作息时间表app满意么&#xff1f; 答&#xff1a;非常不满意&#xff0c;我们的设想是让他更智能&#xff0c;更多的提醒方式&#xff0c;更好的交互体验。如果作为一个闹钟他是非常不合格的&#xff0…

R可视化数据必要格式——长格式

一、引言 我们在对数据进行可视化时遇到最头疼、最常见的问题是什么&#xff1f;数据问题。 因为我们往往不会从零自己编程进行可视化&#xff0c;往往是现有模板或积累&#xff0c;而正确的数据格式对应正确的图形包要求&#xff0c;一定会正确出图&#xff0c;所以只有一个问…

调试 hipcc 的llvm llc gpu目标代码生成模块

源码&#xff1a; hello_vectorAdd.hip: __global__ void vectorAdd(const float *A, const float *B, float *C) {int i blockDim.x * blockIdx.x threadIdx.x;C[i] A[i] B[i] 0.0f; } Makefile: x.O1.s: hello_vectorAdd.hip../../local_amdgpu/bin/clang ./hello_vec…

力扣hot100-普通数组2

文章目录 题目&#xff1a;轮转数组方法1-使用额外的数组方法2-三次反转数组 除自身以外数组的乘积方法1-用到了除法方法2-前后缀乘积法 题目&#xff1a;轮转数组 原题链接&#xff1a;轮转数组 方法1-使用额外的数组 方法1是自己写出来的。方法2参考的别人的&#xff0c;…

AttackGen:一款基于LLM的网络安全事件响应测试工具

关于AttackGen AttackGen是一款功能强大的网络安全事件响应测试工具&#xff0c;该工具利用了大语言模型和MITRE ATT&CK框架的强大功能&#xff0c;并且能够根据研究人员选择的威胁行为组织以及自己组织的详细信息生成定制化的事件响应场景。 功能介绍 1、根据所选的威胁行…

【MindSpore学习打卡】应用实践-计算机视觉-FCN图像语义分割-基于MindSpore实现FCN-8s进行图像语义分割的教程

图像语义分割是计算机视觉领域中的一个重要任务&#xff0c;它旨在对图像中的每个像素进行分类&#xff0c;从而实现对图像内容的详细理解。在众多图像语义分割算法中&#xff0c;全卷积网络&#xff08;Fully Convolutional Networks, FCN&#xff09;因其端到端的训练方式和高…

7.7、指针和函数

代码 #include <iostream> using namespace std;//实现两个数字进行交换 void swap01(int a, int b) {int temp a;a b;b temp;cout << "swap01a " << a << endl;cout << "swap01b " << b << endl; }void sw…

08 docker Registry搭建docker私仓

目录 本地镜像发布流程 1. docker pull registry 下载镜像 2. docker run 运行私有库registry 3. docker commit 构建镜像 4. docker tag 修改新镜像&#xff0c;使之符合私服规范tag 5. 修改配置文件使之支持http 6. curl验证私服库上有什么镜像 7. push推送 pull拉取 …

Activity、Window、DecorView的关系

目录 一、Activity、Window、DecorView的层级关系如下图所示&#xff1a; 1、Activity 2、Window 3、DecorView 二、DecorView初始化相关源码 三、DecorView显示时机 前言&#xff1a; 不同的Android版本有差异&#xff0c;以下基于Android 11进行讲解。 一、Activi…