微服务实战——登录(普通登录、社交登录、SSO单点登录)

登录

1.1. 用户密码

@PostMapping("/login")
    public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session){
        R r = memberFeignService.login(vo);
        if(r.getCode() == 0){
            MemberRespVo data = r.getData("data", new TypeReference<MemberRespVo>() {
            });
            session.setAttribute("loginUser", data);
            return "redirect:http://gulimall.com";
        }else {
            Map<String, String> errors = new HashMap<>();
            errors.put("msg", r.getData("msg", new TypeReference<String>(){}));
            redirectAttributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }

    @GetMapping("/login.html")
    public String loginPage(HttpSession session){
        Object loginUser = session.getAttribute("loginUser");
        if(loginUser != null){
            return "redirect:http://gulimall.com";
        }
        return "login";
    }
@Override
    public MemberEntity login(MemberLoginVo vo) {

        String loginacct = vo.getLoginacct();
        String password = vo.getPassword();
        MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>()
                .eq("username", loginacct)
                .or().eq("mobile", loginacct));
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if(memberEntity == null){
            return null;
        }
        if (passwordEncoder.matches(password, memberEntity.getPassword())) {
            return memberEntity;
        }
        return null;
    }

1.2. 社交登录

QQ 、微博、 github 等网站的用户量非常大,别的网站为了简化自我网站的登陆与注册逻辑,引入社交登陆功能;

步骤:

1 )、用户点击 QQ 按钮

2 )、引导跳转到 QQ 授权页

3)、用户主动点击授权,跳回之前网页。

1.2.1. OAuth2.0
  • OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
  • OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。

  • 官方版流程:

(A )用户打开客户端以后,客户端要求用户给予授权。

(B )用户同意给予客户端授权。

(C )客户端使用上一步获得的授权,向认证服务器申请令牌。

(D )认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E )客户端使用令牌,向资源服务器申请获取资源。

(F )资源服务器确认令牌无误,同意向客户端开放资源。

OAuth2.0流程:

  • 使用Code换取AccessToken,Code只能用一次
  • 同一个用户的accessToken一段时间是不会变化的,即使多次获取
1.2.2. 代码实现

1)、进入微博开放平台

微博组件_微博开放平台

2)、添加社交登录回调接口

认证接口

  • 通过HttpUtils发送请求获取token,并将token等信息交给member服务进行社交登录
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页

修改“com.cwh.gulimall.auth.feign.MemberFeignService”类,代码如下:

@PostMapping("/member/member/oauth2/login")
public R oauth2Login(@RequestBody SocialUser socialUser);

添加“com.cwh.gulimall.auth.vo.SocialUser”类,代码如下:

package com.cwh.gulimall.auth.vo;
 
import lombok.Data;
 
@Data
public class SocialUser {
    private String access_token;
    private String remind_in;
    private long expires_in;
    private String uid;
    private String isRealName;
}

添加“com.cwh.gulimall.auth.vo.MemberResponseVO”类,代码如下:

package com.cwh.gulimall.auth.vo;
 
import lombok.Data;
import lombok.ToString;
 
import java.util.Date;
 
@ToString
@Data
public class MemberResponseVO {
    private Long id;
    /**
     * 会员等级id
     */
    private Long levelId;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 昵称
     */
    private String nickname;
    /**
     * 手机号码
     */
    private String mobile;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 头像
     */
    private String header;
    /**
     * 性别
     */
    private Integer gender;
    /**
     * 生日
     */
    private Date birth;
    /**
     * 所在城市
     */
    private String city;
    /**
     * 职业
     */
    private String job;
    /**
     * 个性签名
     */
    private String sign;
    /**
     * 用户来源
     */
    private Integer sourceType;
    /**
     * 积分
     */
    private Integer integration;
    /**
     * 成长值
     */
    private Integer growth;
    /**
     * 启用状态
     */
    private Integer status;
    /**
     * 注册时间
     */
    private Date createTime;
 
    private String socialUid;
 
    private String accessToken;
 
    private long expiresIn;
}

添加“com.cwh.gulimall.auth.controller.Oauth2Controller”类,代码如下:

@Controller
public class OauthController {

    @Autowired
    private MemberFeignService memberFeignService;

    @RequestMapping("/oauth2.0/weibo/success")
    public String authorize(String code, RedirectAttributes attributes) throws Exception {
        // 1、使用code换取token,换取成功则继续2,否则重定向至登录页
        Map<String, String> query = new HashMap<>();
        query.put("client_id", "2144***074");
        query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");
        query.put("grant_type", "authorization_code");
        query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");
        query.put("code", code);
        // 发送post请求换取token
        HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());
        Map<String, String> errors = new HashMap<>();
        if (response.getStatusLine().getStatusCode() == 200) {
            // 2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页
            String json = EntityUtils.toString(response.getEntity());
            SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {
            });
            R login = memberFeignService.login(socialUser);
            // 2.1 远程调用成功,返回首页并携带用户信息
            if (login.getCode() == 0) {
                String jsonString = JSON.toJSONString(login.get("memberEntity"));
                MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {
                });
                attributes.addFlashAttribute("user", memberResponseVo);
                return "redirect:http://gulimall.com";
            }else {
                // 2.2 否则返回登录页
                errors.put("msg", "登录失败,请重试");
                attributes.addFlashAttribute("errors", errors);
                return "redirect:http://auth.gulimall.com/login.html";
            }
        }else {
            errors.put("msg", "获得第三方授权失败,请重试");
            attributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }

登录接口

  • 登录包含两种流程,实际上包括了注册和登录
  • 如果之前未使用该社交账号登录,则使用token调用开放api获取社交账号相关信息,注册并将结果返回
  • 如果之前已经使用该社交账号登录,则更新token并将结果返回

添加“com.cwh.gulimall.member.vo.SocialUser”类,代码如下:

package com.cwh.gulimall.member.vo;
 
import lombok.Data;
 
@Data
public class SocialUser {
    private String access_token;
    private String remind_in;
    private long expires_in;
    private String uid;
    private String isRealName;
}
修改“com.cwh.gulimall.member.controller.MemberController”类,代码如下:

    @PostMapping("/oauth2/login")
    public R oauth2Login(@RequestBody SocialUser socialUser){
        MemberEntity entity = memberService.login(socialUser);
        if (entity != null){
            return R.ok().setData(entity);
        }else {
            return R.error(BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getMsg());
        }
 
    }

修改gulimall_ums.ums_member表结构,sql如下:

ALTER TABLE `gulimall_ums`.`ums_member` 
ADD COLUMN `social_uid` varchar(255) NULL COMMENT '社交用户id' AFTER `create_time`,
ADD COLUMN `access_token` varchar(255) NULL COMMENT '访问token' AFTER `social_uid`,
ADD COLUMN `expires_in` int NULL COMMENT '过期时间戳' AFTER `access_token`;

修改“com.cwh.gulimall.member.entity.MemberEntity”类,新增三个属性,代码如下:

修改“com.cwh.gulimall.member.service.MemberService”类,代码如下:

MemberEntity login(SocialUser socialUser);

修改“com.cwh.gulimall.member.service.impl.MemberServiceImpl”类,代码如下:

@Override
public MemberEntity login(SocialUser socialUser) {
// 1 根据 uid 判断当前用户是否以前用社交平台登录过系统
MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", socialUser.getUid()));
if (!StringUtils.isEmpty(memberEntity)) {
    // 说明这个用户之前已经注册过
    MemberEntity update = new MemberEntity();
    update.setId(memberEntity.getId());
    update.setAccessToken(socialUser.getAccess_token());
    update.setExpiresIn(socialUser.getExpires_in());
    this.baseMapper.updateById(update);

    memberEntity.setAccessToken(socialUser.getAccess_token());
    memberEntity.setExpiresIn(socialUser.getExpires_in());
    return memberEntity;
} else {
    // 未找到则注册 根据社交平台的开放接口查询用户的开放信息存储到系统
    MemberEntity register = new MemberEntity();
    try {
        Map<String, String> query = new HashMap<>();
        query.put("access_token", socialUser.getAccess_token());
        query.put("uid", socialUser.getUid());
        HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);
        if (response.getStatusLine().getStatusCode() == 200) {
            String json = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSON.parseObject(json);
            String name = jsonObject.getString("name");
            String gender = jsonObject.getString("gender");
            // ......
            register.setNickname(name);
            register.setGender("m".equals(gender) ? 1 : 0);
            // .....
        }
    } catch (Exception e) {
        log.warn("调用微博接口获取信息异常{}", e);
    }
    register.setSocialUid(socialUser.getUid());
    register.setAccessToken(socialUser.getAccess_token());
    register.setExpiresIn(socialUser.getExpires_in());
    this.baseMapper.insert(register);
    return register;
}

小结

Oauth2.0 ;授权通过后,使用 code 换取 access_token ,然后去访问任何开放 API

1 )、 code 用后即毁

2 )、 access_token 在几天内是一样的

3 )、 uid 永久固定

1.3. SpringSession

1.3.1. Session共享问题
1.3.1.1. session原理

jsessionid相当于银行卡,存在服务器的session相当于存储的现金,每次通过jsessionid取出保存的数据。

问题:但是正常情况下session不可跨域,它有自己的作用范围

1.3.1.2. 分布式下session共享问题

  • 同一个服务,复制多份,session不同步问题
  • 不同服务,session不能共享问题
1.3.2. Session共享问题解决
1.3.2.1. session复制

1.3.2.2. 客户端存储

1.3.2.3. hash一致性

1.3.2.4. 统一存储

1.3.2.5. 不同服务,子域session共享

1.3.3. SpringSession整合redis

通过SpringSession修改session的作用域

1.3.3.1. 环境搭建

gulimall-auth-server模块

pom导入依赖

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

修改apllication.properties配置

spring.session.store-type=redis

主配置类添加注解@EnableRedisHttpSession

修改“com.cwh.gulimall.auth.GulimallAuthServerApplication”类,代码如下:

@EnableRedisHttpSession
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallAuthServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(GulimallAuthServerApplication.class, args);
    }
}
1.3.3.2. 自定义配置
1.3.4. SpringSession核心原理

核心原理:

1)、@EnableRedisHttpSession导入RedisHttpSessionConfiguration.class配置
1、给容器中添加了一个组件
SessionRepository=》》》 【RedisIndexedSessionRepository】=>redis操作session.session的增删改查的封装类
2、SessionRepositoryFilter=》Filter: session存储过滤器,每个请求过来都必须经过filter
1、创建的时候,就自动从容器中获取到了SessionRepository:
2、原生的request,response都被包装。SessionRepositoryRequestWrapper,SessionRepositoryResponseWrapper
3、以后获取session.request.getSession()
4、wrapperedRequest.getSession();===>SressionRepository中获取到

自动延期。redis中的数据也是有过期时间的

装饰者模式 - SessionRepositoryFilter

  • 原生的获取session时是通过HttpServletRequest获取的
  • 这里对request进行包装,并且重写了包装request的getSession()方法
1.3.5. 页面调整
1.3.5.1. 只要登录成功,缓存有用户数据,再点击登录链接,直接调转到首页;把GulimallWebConfig登录页的映射注释掉

修改“com.cwh.gulimall.auth.controller.LoginController”类,代码如下:

@GetMapping("/login.html")
public String loginPage(HttpSession session){
Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
if (attribute == null) {
    //没登录
    return "login";
} else{
    return "redirect:http://gulimall.com";
}
}
1.3.5.2. 账号密码方式登录也要显示用户名

正常登录也要显示用户名,返回时也要给他放入用户信息

修改“com.cwh.gulimall.auth.controller.LoginController”类,代码如下:

@PostMapping("/login")
public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session) {
    log.info("登录请求参数:{}", JSON.toJSONString(vo));
    //远程登录
    R r = memberFeignService.login(vo);
    if (r.getCode() == 0) {
        MemberResponseVO loginUser = r.getData(new TypeReference<MemberResponseVO>() {
        });
        // 成功放到session中
        session.setAttribute(AuthServerConstant.LOGIN_USER, loginUser);
        return "redirect:http://gulimall.com";
    } else {
        Map<String, String> errors = new HashMap<>();
        errors.put("msg", r.getData("msg", new TypeReference<String>() {
        }));
        redirectAttributes.addFlashAttribute("errors", errors);
        return "redirect:http://auth.gulimall.com/login.html";
    }
}
1.3.5.3. 只要登陆成功每个页面都显示用户名

gulimall-search服务页面显示用户名,需要先搭建好SpringSession环境

导入依赖

     <!--整合SpringSession完成session共享问题-->
    <dependency>
        <groupId>org.springframework.session</groupId>

        <artifactId>spring-session-data-redis</artifactId>

    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-redis</artifactId>

    </dependency>

修改配置

#配置redis
spring.redis.host=192.168.119.127
spring.redis.port=6379
#session存储格式
spring.session.store-type=redis

加注解

添加SpringSession配置类

@Configuration
public class GulimallSessionConfig {
    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("gulimall.com");
        cookieSerializer.setCookieName("GULISESSION");
        return cookieSerializer;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }
}

1.4. SSO 单点登录

Single Sign On 一处登陆、处处可用

前置概念

1 )、单点登录业务介绍

早期单一服务器,用户认证。

缺点:单点性能压力,无法扩展

分布式,SSO(single sign on)模式

多系统

解决 :

  • 用户身份信息独立管理,更好的分布式管理。
  • 可以自己扩展安全策略
  • 跨域不是问题

缺点:

  • 认证服务器访问压力较大。

gitee参考项目:xxl-sso: 一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、Cookie+Token均支持、Web+APP均支持"等特性;。现已开放源代码,开箱即用。

xxl-sso流程:

  • /xxl-sso-server 登录服务器 8080 ssoserver.com
  • /xxl-sso-web-sample-springboot 项目1 8081 client1.com
  • /xxl-sso-web-sample-springboot 项目2 8082 client2.com
#----------sso----------
127.0.0.1 ssoserver.com
127.0.0.1 client1.com
127.0.0.1 client2.com

核心:三个系统即使域名不一样,想办法给三个系统同步同一个用户的票据;

1)、中央认证服务器;ssoserver.com

2)、其他系统,想要登录去ssoserver.com登录,登录成功跳转回来

3)、只要有一个登录,其他都不用登录

4)、全系统统一一个sso-sessionid;所有系统可能域名都不相同

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

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

相关文章

英伟达股价分析:英伟达股价能否上涨到150美元,接下来该如何操作?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经​ 猛兽财经核心观点&#xff1a; &#xff08;1&#xff09;华尔街投行Oppenheimer已将英伟达的目标价上调到了150美元。 &#xff08;2&#xff09;产品方面的最新进展和合作伙伴关系进一步提升了英伟达的市场地位。 &…

Nacos配置管理和Nacos集群配置

目录 Nacos作为配置中心实现配置管理 统一配置管理 如何在nocas添加配置文件 在微服务拉取nacos配置中心的配置 1&#xff09;引入nacos-config依赖 2&#xff09;添加bootstrap.yaml 3&#xff09;测试&#xff0c;读取nacos配置中心中配置文件的内容 ​编辑 总结&…

ORA-65096:公用用户名或角色名无效

CREATE USER DATA_SHARING IDENTIFIED BY "Ab2"; Oracle建立用户的的时候&#xff0c;可能会出现一直提示 ORA-65096:公用用户名或角色名无效&#xff1b; 我查了一下&#xff0c;好像是 oracle 12版本及以上版本的特性&#xff0c;用户名必须加c##或者C##前缀才能创…

拆解学习【反激-PD-氮化镓】(一)

小米67W桌面快充插座&#xff1a; 反激基本拓扑&#xff1a; 商用场景下&#xff0c;这个拓扑进行了如下优化&#xff1a; 1.Q22换成了氮化镓开关管&#xff0c;当然需要适配的能驱动氮化镓的控制芯片 2.D21二极管换成了MOS管。 3.由于是AC220V输入&#xff0c;设计了整流桥…

Android Camera系列(四):TextureView+OpenGL ES+Camera

别人贪婪时我恐惧&#xff0c;别人恐惧时我贪婪 Android Camera系列&#xff08;一&#xff09;&#xff1a;SurfaceViewCamera Android Camera系列&#xff08;二&#xff09;&#xff1a;TextureViewCamera Android Camera系列&#xff08;三&#xff09;&#xff1a;GLSur…

【Nginx系列】Nginx启动失败

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

轻量服务器和云服务器ecs哪个好用一些?

轻量服务器和云服务器ecs哪个好用一些&#xff1f;轻量服务器与云服务器ECS在多方面存在显著差异&#xff0c;对于需要高性能计算和大规模数据处理的用户来说&#xff0c;ECS可能是更好的选择&#xff1b;而对于预算有限且需求较为简单的用户来说&#xff0c;轻量服务器可能更为…

Cpp::STL—list类的模拟实现(上)(13)

文章目录 前言一、结点类的实现二、迭代器类的实现迭代器类的存在意义迭代器类的模板参数构造函数运算符的重载--运算符的重载、!运算符的重载*运算符的重载->运算符的重载 总结 前言 注意本篇难度偏高&#xff0c;其主要体现在迭代器类的实现&#xff01;   什么&#xf…

QD1-P8 HTML 格式化标签(font、pre、b、strong、i、u、del、s、sub、sup)

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…

JAVA-数据结构-排序

1.直接插入排序 1.原理&#xff1a;和玩扑克牌一样&#xff0c;从左边第二个牌开始&#xff0c;选中这个&#xff0c;和前面的所有牌比较&#xff0c;插在合适的位置 public static void insertsort(int[] arr){//直接插入排序for (int i 1; i < arr.length; i) {//此循环…

LOID:有效提升遮挡条件下的车道检测精度

1.论文信息 论文标题&#xff1a;LOID: Lane Occlusion Inpainting and Detection for Enhanced Autonomous Driving Systems 作者&#xff1a;Aayush Agrawal, Ashmitha Jaysi Sivakumar, Ibrahim Kaif∗, Chayan Banerjee† 作者单位&#xff1a;印度马德拉斯印度理工学院&…

Web安全 - 路径穿越(Path Traversal)

文章目录 OWASP 2023 TOP 10导图定义路径穿越的原理常见攻击目标防御措施输入验证和清理避免直接拼接用户输入最小化权限日志监控 ExampleCode漏洞代码&#xff1a;路径穿越攻击案例漏洞说明修复后的安全代码代码分析 其他不同文件系统下的路径穿越特性Windows系统类Unix系统&a…

【C++】基于红黑树封装set和map

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…

为什么很多人宁愿加钱买港版,也不愿买国行 iPhone 16

最近的 iPhone 16 市场&#xff0c;真的是倒反天罡&#xff0c;攻守异形啊。 过去&#xff0c;港版 iPhone 都是性价比的次选&#xff0c;便宜个 10% 都得考虑考虑。但今年&#xff0c;港版 iPhone 16 的价格&#xff0c;反而比国行还贵。 比如&#xff0c;闲鱼上某个卖家&am…

[红队apt]文件捆绑攻击流程

免责声明:本文用于了解攻击者攻击手法&#xff0c;切勿用于不法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理黑客通过文件捆绑进行攻击的流程思路 文件捆绑原理 废话只多说这一句。 1.exe和2.exe被你捆绑为3.exe。 那么你点击了3.exe就等于点…

kafka消息队列核心内容及常见问题

目录 1. 使用消息队列的目的&#xff08;优点与缺点&#xff09; 2. 常见各消息队列对比 3. kafka介绍 3.1 kafka简介 3.2 kafka特点 3.3 kafka系统架构 3.4 设置数据可靠性 3.4.1 Topic 分区副本 3.4.2 消息确认机制 4. 常见问题&#xff08;面试题&#xff09; 4.…

Acwing 排序

1.快速排序 主要思想&#xff1a;基于分治思想。通过选择一个基准元素&#xff0c;将数组分为两部分&#xff0c;左边部分元素都小于等于基准&#xff0c;右边部分元素都大于等于基准。然后对这两部分分别递归地进行排序。 分区逻辑&#xff1a;双指针算法 左指针i从左往右找…

《RabbitMQ篇》消息应答和发布确认

消息应答 消息应答机制&#xff1a;消费者接收信息并处理完之后&#xff0c;告诉rabbitmq该信息已经处理&#xff0c;rabbitmq可以把该信息删除了. 消息自动重新入队&#xff1a;如果处理某个消息的消费者异常关闭了&#xff0c;没有发送ACK确认&#xff0c;rabbitmq会将其重…

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…

postgresql 安装

一、下载 PostgreSQL: File Browser 下载地址 PostgreSQL: File Browser 上传到服务器,并解压 二、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel 创建postgresql 和目录 useradd …