SpringSecurity整合Oauth2.0

news2024/10/6 18:34:47

SpringSecurity整合Oauth2.0

  • 一、概述与原理
    • 1.1 、OAuth2.0 是什么?
    • 1.2、OAuth2.0中角色解释
    • 1.3、OAuth2.0的4中授权模式
      • 1.3.1、授权码模式(重点)
        • 1.3.1.1 原理
        • 1.3.1.2 代码
      • 1.3.2、密码模式(重点)
        • 1.3.2.1 原理
        • 1.3.2.2 代码
      • 1.3.3、简化模式(了解)
      • 1.3.4、客户端模式(了解)
    • 1.4 、SpringSecurity OAuth2.0 整合JWT
      • 1.4.1、整合JWT
      • 1.4.2 、JWT内容扩展
      • 1.4.3 、解析JWT
      • 1.4.4、刷新JWT
    • 1.5 分布式单点登陆
      • 1.5.1 SpringSecurity+ JWT(Token)+Redis 实现分布式单点登陆流程图
      • 1.5.2 SpringSecurity+ OAuth2.0+JWT(Token) 实现分布式【第三方】单点登陆流程图

一、概述与原理

1.1 、OAuth2.0 是什么?

OAuth(OpenAuthorization)是一个开放标准,允许用户能够让第三方应用访问该用户在某一网站上的存储的私密资源(用户信息、照片、视频、联系人列表、好友列表等),而无需将用户名和密码提供给第三方应用,Oauth仅需要提供一个令牌就可以访问用户存储在特定服务上的资源。
每一个令牌授权一个特定的网站在特定时段内访问特定的资源,OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。OAuth2.0 为简化客户端开发提供了特定的授权流,包括 Web 应用、桌面应用、移动端应用等。

1.2、OAuth2.0中角色解释

在OAuth2.0的有四个角色需要清楚

Authorization Server认证服务器。用于认证用户,颁发token。如果客户端认证通过,则发放访问资源服务器的令牌
Resource Server资源服务器。拥有受保护资源,对非法请求拦截,对请求token解析。如果请求包含正确的访问令牌,则可以访问资源
Client客户端。它请求资源服务器时,会带上访问令牌,从而成功访问资源
Resource Owner资源拥有者。最终用户,他有访问资源的账号与密码(指的是用户)

1.3、OAuth2.0的4中授权模式

gitee项目源码练习链接:https://gitee.com/hhgs_admin/springsecurityoauth2-demo
项目git log实操内容
在这里插入图片描述

1.3.1、授权码模式(重点)

1.3.1.1 原理

授权码模式:指的是当前应用先向第三方认证系统申请一个授权码,然后再用该码在第三方认证系统中获取令牌,之后每次访问系统资源的时候,将该令牌传到第三方认证授权服务器上进行校验,当前应用本身不具备校验令牌有效性的功能。
这种模式多应用在第三方登录, 例如jd,bilibili使用第三方:微信或qq登录

流程示意图:
在这里插入图片描述
(1)、资源拥有者(客户)打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,客户端给第三方认证服务器发送请求认证,例如:

http://localhost:8080/oauth/authorize?client_id=p2pweb&response_type=code&scope=app&
redirect_uri=http://xx.xx/notify

参数列表如下:

参数解释说明
client_id客户端接入标识。
response_type授权码模式固定为code。
scope客户端权限
redirect_uri跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。

(2)、浏览器出现向授权服务器授权页面,之后将用户同意授权。

(3)、授权服务器将授权码(AuthorizationCode)转经浏览器发送给client(通过redirect_uri)。

(4)、客户端拿着授权码向授权服务器索要访问access_token,请求如下:

http://localhost:8080/oauth/token?client_id=p2pweb&client_secret=gdjbcd&grant_type=authorization_code&
code=5PgfcD&redirect_uri=http://xx.xx/notify

参数列表如下:

参数说明
client_id客户端准入标识。
client_secret客户端秘钥。
grant_type授权类型,填写authorization_code,表示授权码模式
code授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
redirect_uri申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致

(5)、授权服务器返回令牌(access_token)
.
授权码模式【第三方登录常用模式】:

这种模式是四种模式中最安全的一种模式。一般用于Web服务器端应用或第三方的原生App调用资源服务的时候。
因为在这种模式中access_token不会经过浏览器或移动端的App,而是直接从服务端去交换,这样就最大限度的减小了令牌泄漏的风险。
授权码模式不足的地方是每次都要网路请求去校验令牌,这可能导致在高并发的情况下造成系统瓶颈,这里优化点在于令牌可以在当前系统进行校验,后续可以采用JWT的方式存储令牌信息。

就拿京东直接第三方微信登录示例京东需要在微信开放平台上注册京东商城信息,填写好回调地址redirect_uri,微信平台会给京东生成appid、appsecret(备案)两个标识,这是在微信后台认证服务中要用到的两个重要唯一的标识

接下来用一张流程图来演示一下授权码模式的流程:例子:京东应用使用第三方微信登录
在这里插入图片描述

1.3.1.2 代码

1,导入依赖

      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
      </dependency>
      
      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
       </dependency>
       
     <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR12</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

核心配置文件目录如下:
在这里插入图片描述

2,配置SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/auth/**", "/login/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}

3,配置认证服务器:AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型
                .authorizedGrantTypes("authorization_code");
    }
}

4,配置资源服务器:ResourceServiceConfig

@Configuration
@EnableResourceServer
public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 对/user路径进行安全校验,其他路径放行
        http.requestMatchers().antMatchers("/user/**")
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated();
    }
}

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.3.2、密码模式(重点)

1.3.2.1 原理

密码模式使用较多,适应于第一方的单页面应用以及第一方的原生App,比如:闪聚支付平台运营平台用户使用此模式完成用户登录。 密码模式认证流程如下:
在这里插入图片描述

(1)资源拥有者将用户名、密码发送给客户端

(2)客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌(access_token),请求如下:

http://localhost:8080/oauth/token?/uaa/oauth/token?client_id=p2pweb&client_secret=fgsdgrf&
grant_type=password&username=shangsan&password=123456

参数列表如下:

参数说明
client_id客户端准入标识。
client_secret客户端秘钥。
grant_type授权类型,填写password表示密码模式
username资源拥有者用户名。
password资源拥有者密码

(3)授权服务器将令牌(access_token)

发送给client 这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了client,因此这就说明这种模式只能用于client是 我们自己开发的情况下。
因此密码模式一般用于我们自己开发的,第一方原生App或第一方单页面应用。

1.3.2.2 代码

1,配置SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    // 密码模式对比授权码模式,多增加了这个bean
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/auth/**", "/login/**", "/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}

2,配置AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserService userService;
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * @description: 使用密码模式所需配置
     * @author liyonghui
     * @date 2021/12/5 14:27
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).userDetailsService(userService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型(authorization_code:令牌模式)
//                .authorizedGrantTypes("authorization_code")
                //授权类型-使用密码模式
                .authorizedGrantTypes("password");
    }
}

ResourceServiceConfig配置不变

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.3.3、简化模式(了解)

在这里插入图片描述简化如下:相对于授权码模式,简化了拿授权码这一步操作,直接向授权服务器中拿令牌
在这里插入图片描述
(1)资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会附加客户端的身份信息如:

http://localhost:8080/oauth/authorize? client_id=p2pweb&response_type=token&
scope=app&redirect_uri=http://xx.xx/notify

参数列表如下:

参数解释说明
client_id客户端接入标识。
response_type简化模式固定为token。
scope客户端权限。
redirect_uri跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。

参数描述同授权码模式 ,注意response_type=token,说明是简化模式。

(2)浏览器出现向授权服务器授权页面,之后将用户同意授权。

(3)授权服务器将授权码将令牌(access_token)以Hash的形式存放在重定向uri的fargment中发送给浏览器。注:

fragment 主要是用来标识 URI 所标识资源里的某个资源,在 URI 的末尾通过 (#)作为 fragment 的开头, 其中 # 不属于 fragment 的值。如https://domain/index#L18这个 URI 中 L18 就是 fragment 的值。大家只需要 知道js通过响应浏览器地址栏变化的方式能获取到fragment 就行了。
一般来说,简化模式用于第三方单页面应用。

1.3.4、客户端模式(了解)

在这里插入图片描述
(1)客户端向授权服务器发送自己的身份信息,并请求令牌(access_token)

(2)确认客户端身份无误后,将令牌(access_token)发送给client,请求如下:


http://localhost:8080/oauth/token? client_id=p2pweb&client_secret=fgsdgrf&
grant_type=password&username=shangsan&password=123456 

http://localhost:8080/oauth/token?client_id=p2pweb&client_secret=fdafdag&grant_type=client_credentials

参数列表如下:

参数说明
client_id客户端准入标识
client_secret客户端秘钥。
grant_type授权类型,填写client_credentials表示客户端模式

这种模式是最方便但最不安全的模式。
因此这就要求我们对client完全的信任,而client本身也是安全的。
因此这种模式一般用来提供给我们完全信任的服务器端服务。比如,合作方系统对接,拉取一组用户信息。
客户端模式适应于没有用户参与的,完全信任的一方或合作方服务器端程序接入。

1.4 、SpringSecurity OAuth2.0 整合JWT

gitee项目源码练习链接:https://gitee.com/hhgs_admin/springsecurityoauth2-demo
在这里插入图片描述

1.4.1、整合JWT

了解JWT是干啥的?: https://blog.csdn.net/qq_45399396/article/details/121721822

1,首先了解Oauth2.0中关于JWT配置的两个关键:JwtAccessTokenConverter 、TokenStore

JwtAccessTokenConverter: 帮助程序在JWT编码的令牌值和OAuth身份验证信息之间进行转换,把自己设置的jwt签名(密钥)加入accessTokenConverter中。
TokenStore:访问令牌转换器的令牌存储对象,初始化时,需要转换器将jwt的密钥设置进去

配置JwtTokenStoreConfig

@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public TokenStore jwtTokenStore() {
         //创建带与之有关联的访问令牌转换器的令牌存储
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        // 配置JWT使用的秘钥
        jwtAccessTokenConverter.setSigningKey("test_key");
        return jwtAccessTokenConverter;
    }
}

配置AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserService userService;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore jwtTokenStore;
    
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    /**
     * @description: 使用密码模式所需配置
     * @author liyonghui
     * @date 2021/12/5 14:27
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).userDetailsService(userService)
                //配置存储令牌策略
                .tokenStore(jwtTokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
        ;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型(authorization_code:令牌模式)
//                .authorizedGrantTypes("authorization_code")
                //授权类型-使用密码模式
                .authorizedGrantTypes("password")
        ;
    }
}

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.4.2 、JWT内容扩展

1,TokenEnhancer: JWT的内容扩展接口,实现存储访问令牌之前增强访问令牌的策略。

实现TokenEnhancer接口,设置增强内容

public class JwtTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("enhance", "enhance info");
        objectObjectHashMap.put("ceshi", "张三");
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(objectObjectHashMap);
        return oAuth2AccessToken;
    }
}

配置JwtTokenStoreConfig

@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //配置JWT使用的秘钥
        jwtAccessTokenConverter.setSigningKey("test_key");
        return jwtAccessTokenConverter;
    }

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }
}

配置AuthorizationServerConfig

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserService userService;
    @Autowired
    private AuthenticationManager authenticationManager;
   
    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore jwtTokenStore;
    
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    /**
     * @description: 使用密码模式所需配置
     * @author liyonghui
     * @date 2021/12/5 14:27
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置JWT内容增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(delegates);

        endpoints.authenticationManager(authenticationManager).userDetailsService(userService)
//                .tokenStore(redisTokenStore);
                //配置存储令牌策略
                .tokenStore(jwtTokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                .tokenEnhancer(tokenEnhancerChain) ;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://www.baidu.com")
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型(authorization_code:令牌模式)
//                .authorizedGrantTypes("authorization_code")
                //授权类型-使用密码模式
                .authorizedGrantTypes("password") ;
    }
}

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.4.3 、解析JWT

主要通过Jwts的paese方法,设置密钥,设置token,通过body()方法拿到解析后的主体

@RestController
@RequestMapping("/user")
public class UserController {
    /**
     * @description: 解析JWT
     * @author liyonghui
     * @date 2021/12/5 16:18
     */
    @RequestMapping("getCurrentUser1")
    public Object getCurrentUser1(Authentication authentication, HttpServletRequest request) {
        String head = request.getHeader("Authorization");
        String token = head.substring(head.indexOf("bearer") + 7);
        return Jwts.parser().setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(token).getBody();
    }
}

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.4.4、刷新JWT

这个配置起来简单,就是授权模式配置中,加入刷新令牌的方式,则可以通过refresh_token再去请求获取新的令牌

 @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("admin")
                //配置client-secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
//                .accessTokenValiditySeconds(3600)
                //配置刷新令牌的有效期
                .refreshTokenValiditySeconds(864000)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://localhost:8081/login")
                //自动授权
                .autoApprove(true)
                //配置申请的权限范围
                .scopes("all")
                //配置grant_type,表示授权类型(authorization_code:令牌模式)
//                .authorizedGrantTypes("authorization_code")
                //授权类型-使用密码模式(添加了刷新令牌 refresh_token)
                .authorizedGrantTypes("password","refresh_token","authorization_code")
        ;
    }

具体测试在gitee上源码项目和md文档中给出具体的测试流程及截图
.

1.5 分布式单点登陆

1.5.1 SpringSecurity+ JWT(Token)+Redis 实现分布式单点登陆流程图

在这里插入图片描述

1.5.2 SpringSecurity+ OAuth2.0+JWT(Token) 实现分布式【第三方】单点登陆流程图

基于第三方登录
在这里插入图片描述

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

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

相关文章

开发运维(DevOps)自动化运维与持续交付企业级实战

一、网站部署流程 1、传统网站部署流程 传统的网站部署,大家在运维过程中,网站部署是运维的工作之一,网站部署的流程大致分为: 需求分析—原型设计—开发代码—提交测试—内网部署—确认上线—备份数据—外网更新-最终测试,如果发现外网部署的代码有异常,需要及时回滚…

[附源码]JAVA毕业设计心理健康系统(系统+LW)

[附源码]JAVA毕业设计心理健康系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

网络安全观察报告恶意软件观察

攻击类型分析 2018 年&#xff0c;主要的攻击类型 1 为 SYN Flood&#xff0c;UDP Flood&#xff0c;ACK Flood&#xff0c;HTTP Flood&#xff0c;HTTPS Flood&#xff0c; 这五大类攻击占了总攻击次数的 96&#xff05;&#xff0c;反射类攻击不足 3%。和 2017 年相比&…

使用分页导入的方式把大量数据从mysql导入es

1、首先要有分页功能的代码 如何使用mybatis-plus实现分页&#xff0c;可参考 http://t.csdn.cn/ddnlk 2、要创建feign远程调用模块 可以参考 http://t.csdn.cn/gshFw 3、在feign模块中声明远程调用接口 1.在feign模块中创建一个接口&#xff0c;名字可以是你要调用的服务名&…

指定区域内实现多尺度、多维度2D图形随机填充(如圆、椭圆、多边形)之MATLAB实现

N久之前&#xff0c;咱在公众号中分享了如何用MATLAB实现在指定区域内随机填充圆&#xff0c;并将相关功能封装一个名为randCircle函数里面&#xff0c;其可实现的功能如下&#xff1a; (1) 设定是否允许填充圆相交、相切或独立存在 (2) 指定区域内圆的生成个数 (3) 设定是否允…

[附源码]计算机毕业设计基于vuejs的文创产品销售平台appSpringboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【面试题】说说 Promise是什么?如何使用

大厂面试题分享 面试题库 前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 前言 本文主要介绍和总结Promise的作用、使用方式和其对应的一些方法,供大家参考学习&#xff0c;如有写的不准确的地方欢迎大家指出&a…

Android 使用 jni Demo示例

Android 使用 jni Demo示例简介1. NDK的介绍1.1 NDK 简介1.2 NDK 特点2. JNI介绍2.1 JNI 简介2.2 为什么要有 JNI&#xff1f;3. NDK 与 JNI 的关系NDK下载及环境配置1. 使用Android studio SDK Manager下载2.配置NDK2.1 配置环境变量2.2 Android studio配置NDK示例Demo流程1.版…

RabbitMQ - 安装和使用

RabbitMQ - 安装和使用一. 安装二. RabbitMQ的简单使用2.1 创建交换机2.1.1 交换机类型2.1.2 持久化方式2.2 创建队列2.3 绑定交换机和队列2.4 SpringBoot整合2.5 另外一种监听写法一. 安装 一键安装&#xff1a; docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 …

rtl8221b+mcu,2.5g光纤收发器的开发备份

1、rtl8221b是一款2.5g的光电转换的phy 系统的构建如下 为了省成本&#xff0c;不用mac来对接其中的gmii接口直接接光模块 2、mdio和mdc由mcu的gpio来模拟&#xff0c;在csdn上有很多的文章来参考 mdio的参数如下 不想看英文可以参考下面的文章 MDIO(clause 22 与 clause 4…

Java基础之《netty(10)—Reactor三种模式》

一、单Reactor单线程模式 1、工作原理图 2、方案说明 &#xff08;1&#xff09;Select是前面I/O复用模型介绍的标准网络编程API&#xff0c;可以实现应用程序通过一个阻塞对象监听多路连接请求。 &#xff08;2&#xff09;Reactor对象通过Select监控客户端请求事件&#xf…

一元钱注册 chatGPT账号

文章目录打开 openai chatgpt 主页注册 chatGPT 账号找境外的电话号码激活账号查看服务价格账号注册充值成功参考视频 打开 openai chatgpt 主页 打开之前首先登录 vpn。但是使用 vpn 有可能还是会被告知 当前国家没有开放服务个人建议&#xff1a; 使用美国的 ip 地址我使用…

PIN TO PIN替代GM8775C|DSI转LVDS转换方案芯片CS5518|CS5518完全替代GM8775C

GM8775C 型 DSI 转双通道 LVDS 发送器产品主要实现将 MIPI DSI 转单/双通道 LVDS 功能&#xff0c;MIPI 支持 1/2/3/4 通道可选&#xff0c;最大支持 4Gbps 速率。LVDS 时钟频率最高 154MHz&#xff0c; 最大支持视频格式为 FULL HD&#xff08;1920 x 1200&#xff09; CS551…

网络安全观察报告

攻击类型分析 2018 年&#xff0c;主要的攻击类型 1 为 SYN Flood&#xff0c;UDP Flood&#xff0c;ACK Flood&#xff0c;HTTP Flood&#xff0c;HTTPS Flood&#xff0c; 这五大类攻击占了总攻击次数的 96&#xff05;&#xff0c;反射类攻击不足 3%。和 2017 年相比&…

测评报告:文件导入哪家强?

文件导入哪家强&#xff1f; 引子 最近业务上遇到一个场景&#xff0c;需要将一个/多个文本文件导入到与其结构对应的表中。功能需求比较简单&#xff0c;大部分的关系数据库基本都支持这个功能。基于上面的场景把手头上的几款开源数据库和国产数据库的文件导入功能进行了性能对…

【通信基础】TTL、RS232、RS485

TTL1、TTL简介RS2321、RS232基本概念2、DB9串口定义及接线参考3、RS232经典电路4、特点RS4851、RS485简介2、特点3、传输距离4、经典电路5、传输差分电平信号TTL 1、TTL简介 TTL的英文全称是Transisor-Transisor Logic. 翻译过来就是晶体管与晶体管之间的逻辑电路。 TTL电平信…

78.【大二实训--《宿舍管理系统》】

大二实训--《宿舍管理系统》1.在JSP中&#xff0c;如果想要获取后端传过来的数据2.在Dao层我们用数据库的属性给实体类赋值的时候3.在一个Servlet中&#xff0c;如果想要使用多个Service4.字符串yyyy-mm-dd格式转换为Date型5.在设置实体类的时候&#xff0c;属性名开头一定要小…

IDEA创建JavaWeb项目并配置Tomcat

本文教给各位使用IDEA创建web项目&#xff0c;配置tomcat进行访问&#xff0c;好了&#xff0c;下面进入正题 IDEA分为社区版和企业版&#xff0c;区别在于&#xff0c;社区版免费&#xff0c;企业版收费&#xff0c;但有30天免费使用期&#xff0c;到期后会提示你进行激活&am…

MySQL 单表查询

1.简单查询 1.1 SELECT语句 SELECT [DISTINCT] * |字段名1&#xff0c;字段名2&#xff0c;... FROM 表名 [WHERE 条件表达式1] [GROUP BY 字段名 [HAVING 条件表达式2]] [GROUP BY 字段名 [ASC | DESC]] [LIMIT [OFFSET] 记录数]1.2 查询指定字段 SELECT 字段1,字段2,... F…

【深入理解 —— js闭包】

&#x1f9c1;个人主页&#xff1a;个人主页 ✌支持我 &#xff1a;点赞&#x1f44d;收藏&#x1f33c;关注&#x1f9e1; 文章目录js闭包&#x1f380; 什么是闭包&#xff1f;&#x1fa70; 执行上下文&#xff08;执行环境&#xff09;&#x1f367;解释闭包的含义&#x…