spring cloud、gradle、父子项目、微服务框架搭建---spring secuity oauth2、mysql 授权(九)

文章目录

    • 一、
    • 二、授权服务
      • 2.1 初始化表结构
      • 2.2 引入依赖
      • 2.3 自定义 用户详情类 UserDetailsService
      • 2.4 授权配置 AuthorizationServerConfiguration
      • 2.5 Web安全配置 WebSecurityConfiguration
      • 2.6 默认生成接口
    • 三、资源服务
      • 3.1 引入依赖
      • 3.2 资源服务 ResourceServerConfig
    • 四、授权测试
      • 4.1 授权码模式
        • (1) 获取code
        • (2)获取授权
      • 4.2 账号密码模式
      • 4.3 请求资源服务,忽略鉴权接口
      • 4.4 请求资源服务,需要鉴权接口
      • 4.5 刷新access_token

一、

新建两个服务
1.授权服务 端口号:11007
2.资源服务 端口号:11004

资源服务可以是订单服务、用户服务、商品服务等等

当然这两个服务也可以合并到一起, 依次顺序AuthorizationServerConfiguration、ResourceServerConfig、WebSecurityConfiguration;

WebSecurityConfiguration 会覆盖ResourceServerConfig的部分配置
例如 authorizeRequests().antMatchers().


二、授权服务

2.1 初始化表结构

sql: https://github.com/spring-attic/spring-security-oauth/blob/main/spring-security-oauth2/src/test/resources/schema.sql

在mysql执行报错时,注意字段为LONGVARBINARY类型,对应mysql的blob类型

表明说明
oauth_client_details客户端账号密码、授权、回调地址等重要信息; 手动插入数据
oauth_access_token存储access_token。 授权成功后自动写入数据,过期后不会删除,但再次获取授权会覆盖
oauth_refresh_token存储refresh_token。授权成功后自动写入数据,过期后不会删除,但再次获取授权会覆盖
oauth_code存储授权码。authorization_code类型的授权code
oauth_approvals存储授权成功的客户端信息。
oauth_client_token存储从服务端获取的token数据。 JdbcClientTokenServices 已经被标记为@Deprecated ,估计已经弃用
clientDetails自定义oauth_client_details表的详情表

oauth_client_token、ClientDetails 暂时无用、可以不用创建

insert一条oauth_client_details数据

INSERT INTO `oauth_client_details` VALUES
 (
 'inside001', 
 NULL, 
 '$2a$10$FCaIYtevbAi5HCXF5PeSVO7zFQgyP7XbPF0zXip7FEL1UrBoE2PyK', 
 'read', 
 'client_credentials,authorization_code,password,refresh_token',
 'http://www.baidu.com', 
 NULL, NULL, NULL, NULL, NULL
 );

2.2 引入依赖

implementation(‘org.springframework.cloud:spring-cloud-starter-oauth2:3.0.0-RC1’)

2.3 自定义 用户详情类 UserDetailsService

package com.xxxx.oauth.service.impl;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class CustomerUserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String password = new BCryptPasswordEncoder().encode("1");
        return new User(username, password, AuthorityUtils.createAuthorityList("test"));
    }
}

必须继承org.springframework.security.core.userdetails.UserDetailsService
对于loadUserByUsername的实现, 真实业务可能需要验证username,验证roles等等, 现在直接返回不验证

2.4 授权配置 AuthorizationServerConfiguration

package com.xxxx.oauth.oauth2;

import com.xxxx.oauth.service.impl.CustomerUserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.sql.DataSource;
import java.util.concurrent.TimeUnit;


/**
 * 授权服务配置
 *
 * @author Administrator
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private DataSource dataSource;

    /**
     * 存储令牌,采用数据库
     * @return
     */
    @Bean
    public TokenStore JdbcTokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    /**
     * 授权码,采用数据库
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 授权信息,采用数据库
     * @return
     */
    @Bean
    public ApprovalStore jdbcApprovalStore(){
        return new JdbcApprovalStore(dataSource);
    }

    /**
     * 客户端client服务,采用数据库,且设置加密方式
     * @return
     */
    @Bean
    public ClientDetailsService jdbcClientDetailsService(){
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder());
        return jdbcClientDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义 实现用户类
     */
    @Autowired
    private CustomerUserDetailsServiceImpl customerUserDetailsServiceImpl;

    /**
     * 配置端点
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /******令牌服务配置*********************************************************************/
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        // 存储令牌
        tokenServices.setTokenStore(JdbcTokenStore());
        // 是否允许刷新令牌
        tokenServices.setSupportRefreshToken(true);
        // 是否重复使用刷新令牌(直到过期)
        tokenServices.setReuseRefreshToken(true);
        // 客户端client服务
        tokenServices.setClientDetailsService(jdbcClientDetailsService());
        // 用来控制令牌存储增强策略
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        // AccessToken有效时间,优先取数据库中的配置,如果未配置,此处才会生效
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(7));
        // RefreshToken有效时间,优先取数据库中的配置,如果未配置,此处才会生效
        tokenServices.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30));


        /******端点配置*********************************************************************/
        //认证管理器
        endpoints.authenticationManager(authenticationManager);
        //用户数据
        endpoints.userDetailsService(customerUserDetailsServiceImpl);
        //令牌存储
        endpoints.tokenStore(JdbcTokenStore());
        //令牌端点的请求方式, 默认是POST
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
        //令牌服务
        endpoints.tokenServices(tokenServices);
        //authorization_code类型的授权,code写入DB,  不配置此项则默认内存
        endpoints.authorizationCodeServices(authorizationCodeServices());

        endpoints.approvalStore(jdbcApprovalStore());
    }

    /**
     * 配置客户端client
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //client服务
        clients.withClientDetails(jdbcClientDetailsService());
    }

    /**
     * 安全约束
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //允许客户端使用表单方式发送请求token的认证
                //如果未设置,则需要再http的Authorization中设置授权
                .allowFormAuthenticationForClients()
                // 接口/oauth/token_key的权限    默认拒绝所有 denyAll()  已通过身份验证 isAuthenticated()   permitAll() 公开的
                .tokenKeyAccess("isAuthenticated()")
                // 接口/oauth/check_token的权限   默认拒绝所有 denyAll()  已通过身份验证 isAuthenticated()  permitAll() 公开的
                .checkTokenAccess("isAuthenticated()");
    }
}

2.5 Web安全配置 WebSecurityConfiguration

package com.xxxx.oauth.oauth2;

import com.xxxx.oauth.service.impl.CustomerUserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
* @author Administrator
/*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

  /**
   * 自定义 实现用户类
   */
  @Autowired
  public CustomerUserDetailsServiceImpl customerUserDetailsServiceImpl;

  /**
   * 重新实例化 AuthenticationManager Bean
   */
    @Override
    @Bean()
    public AuthenticationManager authenticationManagerBean() throws Exception {
      return super.authenticationManagerBean();
    }

  /**
   * 授权管理配置
   *
   * @param authManager
   * @throws Exception
   */
    @Override
    protected void configure(AuthenticationManagerBuilder authManager) throws Exception {
      authManager.userDetailsService(customerUserDetailsServiceImpl);
    }

  /**
   * 安全约束配置
   *
   * @param webSecurity
   * @throws Exception
   */
   @Override
    public void configure(WebSecurity webSecurity) throws Exception {
      //解决静态资源被拦截的问题
      //web.ignoring().antMatchers("/favicon.ico", "/asserts/**");
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
      httpSecurity
              .userDetailsService(customerUserDetailsServiceImpl).formLogin().permitAll()
              .and().authorizeRequests()
                    // 自定义的一些接口,可匿名访问
                    .antMatchers("/test/**", "/test1/**").permitAll()
                    // 静态文件,可匿名访问
                    .antMatchers( "/**/*.css", "/**/*.js").permitAll()
                    //排除上面可匿名访问外,其他全部需要鉴权认证
                    .anyRequest().authenticated()
              // 关闭跨域保护;
              .and().csrf().disable();
    }
}

2.6 默认生成接口

/oauth/authorize:授权端点 ,固定授权入口路径 ,也是授权服务器的用户允许授权的页面
/oauth/token :获取令牌端点
/oauth/confirm_access:用户确认授权提交端点
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
/oauth/token_key:提供公有密钥的端点,如果使用 JWT 令牌的话


三、资源服务

3.1 引入依赖

implementation(‘org.springframework.cloud:spring-cloud-starter-oauth2:3.0.0-RC1’)

3.2 资源服务 ResourceServerConfig

package com.xxxx.order.oauth2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

/**
 * @author Administrator
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.formLogin().permitAll()
            .and().authorizeRequests()
            // 自定义的一些接口,可匿名访问
            .antMatchers("/hello/sayHello", "/test/**").permitAll()
            //排除上面可匿名访问外,其他全部需要鉴权认证
            .anyRequest().authenticated()
            // 关闭跨域保护;
            .and().csrf().disable();
    }

    /**
     * 资源服务令牌解析服务 配置远程ResourceServerTokenServices后,可不用设置yml远程security.oauth2配置
     *
     * @return
     */
    @Bean
    public ResourceServerTokenServices tokenService() {
        //使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
        RemoteTokenServices service=new RemoteTokenServices();
        service.setCheckTokenEndpointUrl("http://localhost:11007/oauth/check_token");
        service.setClientId("inside001");
        service.setClientSecret("1");

        return service;
    }
}

四、授权测试

4.1 授权码模式

(1) 获取code

浏览器请求授权码:http://localhost:11007/oauth/authorize?client_id=inside001&response_type=code&redirect_uri=http://www.baidu.com

client_id和redirect_uri的参数必须auth_client_details表中插入的数据

登录授权完成后浏览器会重定向到:https://www.baidu.com/?code=R0RtrN 此时R0RtrN为授权码

(2)获取授权

post请求地址: http://localhost:11007/oauth/token
body中参数 x-wwww-form-urlencoded

grant_type:authorization_code
code:R0RtrN    上一步获取的code
client_id:inside001
client_secret:1
redirect_uri:http://www.baidu.com

响应结果:

  {
    "access_token": "b9d0bf39-47d2-43bd-ade4-abf4fdf8cd73",
    "token_type": "bearer",
    "refresh_token": "e0898a7f-f2d5-4d91-81b4-9e259ede88a9",
    "expires_in": 3599,
    "scope": "read"
  }

在这里插入图片描述

4.2 账号密码模式

post请求地址: http://localhost:11007/oauth/token

请求body:
body中参数 x-wwww-form-urlencoded

grant_type:password
client_id:inside001
client_secret:1
username:inside001
password:1

username就是自定义用户详情类CustomerUserDetailsServiceImpl.loadUserByUsername验证处理,目前未验证,可随便输入

响应结果:

{
  "access_token": "b9d0bf39-47d2-43bd-ade4-abf4fdf8cd73",
  "token_type": "bearer",
  "refresh_token": "e0898a7f-f2d5-4d91-81b4-9e259ede88a9",
  "expires_in": 3599,
  "scope": "read"
}

在这里插入图片描述

4.3 请求资源服务,忽略鉴权接口

因为在资源服务的ResourceServerConfig配置中,configure(HttpSecurity http)方法下,.antMatchers(“/hello/sayHello”, “/test/**”).permitAll() 配置了

接口直接请求, 例如:http://localhost:11004/hello/sayHello

4.4 请求资源服务,需要鉴权接口

请求:csshttp://localhost:11004/hello/passwordEncoder?pwd=1

在请求header中添加access_token:Authorization:Bearer 33453e78-cc4b-4ca8-8d5d-bbfd719a1f8b

在这里插入图片描述

4.5 刷新access_token

正常情况下,access_token有效时间小于refresh_token有效时间,access_token失效后可利用refresh_token重新获取access_token, 当refresh_token失效后就需要重新获取授权。

post请求地址: http://localhost:11007/oauth/token

body中参数 x-wwww-form-urlencoded

grant_type:refresh_token
refresh_token:0f941d67-da95-479c-bef4-435b5c823402
client_id:inside001
client_secret:1

在这里插入图片描述





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

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

相关文章

本地镜像管理

查看 用户可以通过docker images命令查看本地所有镜像&#xff0c;如下&#xff1a; 这里一共有五个参数&#xff0c;含义分别如下&#xff1a; REPOSITORY 仓库名称&#xff0c;仓库一般用来存放同一类型的镜像。仓库的名称由其创建者指定。如果没有指定则为<none>。…

爬虫逆向实战(二十七)--某某招标投标网站招标公告

一、数据接口分析 主页地址&#xff1a;某网站 1、抓包 通过抓包可以发现数据接口是page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现&#xff0c;请求参数是一整个密文 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 通…

Python之动态规划

序言 最近在学习python语言&#xff0c;语言有通用性&#xff0c;此文记录复习动态规划并练习python语言。 动态规划&#xff08;Dynamic Programming&#xff09; 动态规划是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家…

【DevOps视频笔记】8. Jenkins 配置

一、Jenkins 入门配置 1. 工具 / 插件 介绍 二、插件和工具配置 1. 配置 JDK 和 Maven Stage 1&#xff1a;将服务器中 JDK 和 Maven 映射到 jenkins 容器中 Stage 2&#xff1a;jenkins 全局配置中 -- 指定JAVA_HOME目录 Stage 3&#xff1a;jenkins 全局配置中 -- 指定…

Vue 项目性能优化 — 实践指南

前言 Vue 框架通过数据双向绑定和虚拟 DOM 技术&#xff0c;帮我们处理了前端开发中最脏最累的 DOM 操作部分&#xff0c; 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM&#xff1b;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题&#xff0c;所…

深度图相关评测网站

文章目录 1 单目/Stereo相关测评网站介绍12 单目/Stereo相关测评网站介绍23 单目/Stereo相关测评网站介绍3 1 单目/Stereo相关测评网站介绍1 https://vision.middlebury.edu/stereo/eval3/ 2 单目/Stereo相关测评网站介绍2 http://www.cvlibs.net/datasets/kitti/eval_stereo…

maven推包The environment variable JAVA_HOME is not correctly set

解决办法&#xff1a; 打开idea查看jdk安装位置 1.在/etc下面创建&#xff08;如果存在就是更新&#xff09;launchd.conf。里面添加一行&#xff1a; setenv JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_351.jdk/Contents/Home #JAVA_HOME后面是我的java安装路径…

vscode GDB 调试linux内核 head.S

遇到的问题 此前参考如下文章 https://zhuanlan.zhihu.com/p/510289859 已经完成了在ubuntu 虚拟机用vscode 调试linux 内核。但是美中不足的是&#xff0c;断点最早只能加在__primary_switched() 函数。无法停在更早的断点上&#xff0c;比如ENTRY(stext) 位置。参考《奔跑吧…

数据库的备份与分类以及日志

目录 1、数据库的概念 1.1、数据备份的重要性 1.2、造成数据丢失的原因 1.3、 数据库备份的分类 1.3.1、从物理与逻辑的角度&#xff0c; 1.3.2、原理图 1.3.3.1 完全备份&#xff1a; 1.3.2.2 差异备份 1.2.3.3、 增量备份 1.3.3、 备份方式比较 1.4、常见的备份方…

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

arm版Linux下安装es集群

背景&#xff1a;由于生产上网络没通&#xff0c;没办法&#xff0c;只能自己安装一个es集群的测试环境了&#xff0c;我的电脑是Mac M2&#xff0c;安装的Linux是centos7&#xff0c;也是arm版的。 第一步&#xff1a;查看自己Linux系统的版本 命令&#xff1a;uname -a 例如…

Midjourney学习(二)参数的基础

prompt的组成 prompt 可以由三部分组成&#xff0c; 第一部分是垫图部分&#xff0c;也就是一张网络图片 第二部分是文本描述内容 第三部分则是参数 参数列表 --aspect <value> 或者 --ar <value> 控制画面的比例&#xff0c;横竖比例 --version <value> -…

批量身份证图片转Excel,核验真伪,保留头像,只需一款软件

你是否曾经遇到过需要将大量员工的身份证图片转化为Excel表格的情况&#xff1f;这种情况可能会让你感到无从下手。但是&#xff0c;现在有了金鸣表格文字识别电脑客户端&#xff0c;一切都将变得轻松便捷。 首先&#xff0c;你只需要前往金鸣识别官网下载并安装金鸣表格文字识…

电缆厂 3D 可视化管控系统 | 图扑数字孪生

近年来&#xff0c;我国各类器材制造业已经开始向数字化生产转型&#xff0c;使得生产流程变得更加精准高效。通过应用智能设备、物联网和大数据分析等技术&#xff0c;企业可以更好地监控生产线上的运行和质量情况&#xff0c;及时发现和解决问题&#xff0c;从而提高生产效率…

vue项目静态文件资源下载

业务场景&#xff1a;页面有一个导入功能&#xff0c;需要一个模板文件供下载&#xff0c;文件放在本地。 对于 Vue 3 Vite 项目&#xff0c;使用 require 方法来导入模块是不被支持的。require 是 CommonJS 规范中用于模块导入的方法&#xff0c;在 Webpack 等构建工具中常用…

编绎和优化,脚本代码小米加步枪赶超英法美

编程达人&#xff1a;冰冻牡蛎 测试&#xff0c;总结》》 今有空&#xff0c;继续看了一下竹笋大师几天前提出的“使用for循环查找10亿内可被7整除的数的个数”的题目&#xff08;相关文件&#xff1a;群文件 10亿以内多少个数字可以整除7.7z &#xff09; 1. 论输出的exe大小…

matlab使用教程(27)—微分代数方程(DAE)求解

1.什么是微分代数方程&#xff1f; 微分代数方程是一类微分方程&#xff0c;其中一个或多个因变量导数未出现在方程中。方程中出现的未包含其导数的变量称为代数变量&#xff0c;代数变量的存在意味着您不能将这些方程记为显式形式 y ′ f t , y 。相反&#xff0c;您可以…

Excel:通过Lookup函数提取指定文本关键词

函数公式&#xff1a;LOOKUP(9^9,FIND($G 2 : 2: 2:G 6 , C 2 ) , 6,C2), 6,C2),G 2 : 2: 2:G$6) 公式解释&#xff1a; lookup第一参数为9^9&#xff1a;代表的是一个极大值的数据&#xff0c;查询位置里面最接近这一个值的数据&#xff1b;lookup第二参数用find函数代替&am…

springboot第37集:kafka,mqtt,Netty,nginx,CentOS,Webpack

image.png binzookeeper-server-start.shconfigzookeeper.properties.png image.png image.png 消费 image.png image.png image.png image.png image.png image.png image.png image.png image.png Netty的优点有很多&#xff1a; API使用简单&#xff0c;学习成本低。功能强大…

JUC并发编程--------基础篇

一、多线程的相关知识 栈与栈帧 我们都知道 JVM 中由堆、栈、方法区所组成&#xff0c;其中栈内存是给谁用的呢&#xff1f;其实就是线程&#xff0c;每个线程启动后&#xff0c;虚拟 机就会为其分配一块栈内存。 每个栈由多个栈帧&#xff08;Frame&#xff09;组成&#xf…