写了一个 SSO 单点登录的代码示例给胖友!

2023-11-19

发危~

摘要: 原创出处 http://www.iocoder.cn/Spring-Security/OAuth2-learning-sso/ 「芋道源码」欢迎转载,保留摘要,谢谢!

  • 1. 概述

  • 2. 搭建统一登录系统

  • 3. 搭建 XXX 系统

  • 666. 彩蛋


本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-68-spring-security-oauth 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

在前面的文章中,我们学习了 Spring Security OAuth 的简单使用。

  • 《芋道 Spring Security OAuth2 入门》

  • 《芋道 Spring Security OAuth2 存储器》

今天我们来搞波“大”的,通过 Spring Security OAuth 实现一个单点登录的功能。

可能会有粉丝不太了解单点登录是什么?单点登录,英文是 Single Sign On,简称为 SSO,指的是当有多个系统需要登录时,用户只需要登录一个统一的登录系统,而无需在多个系统重复登录。

举个最常见的例子,我们在浏览器中使用阿里“全家桶”:

求助信:麻烦有认识阿里的胖友,让他们给打下钱。。。

  • 淘宝:https://www.taobao.com

  • 天猫:https://www.tmall.com

  • 飞猪:https://www.fliggy.com

  • ...

我们只需要在统一登录系统(https://login.taobao.com)进行登录即可,而后就可以“愉快”的自由剁手,并且无需分别在淘宝、天猫、飞猪等等系统重新登录。

登录系统

 

友情提示:更多单点登录的介绍,可见《维基百科 —— 单点登录》。

下面,我们正式搭建 Spring Security OAuth 实现 SSO 的示例项目,如下图所示:

项目结构

 

  • 创建 lab-68-demo21-authorization-server-on-sso 项目,作为统一登录系统

    旁白君:机智的胖友,是不是发现这个项目和授权服务器非常相似!!!

  • 创建 lab-68-demo21-resource-server-on-sso 项目,模拟需要登录的 XXX 系统

    旁白君:机智的胖友,是不是发现这个项目和资源服务器非常相似!!!

2. 搭建统一登录系统

示例代码对应仓库:

  • 统一登录系统:lab-68-demo21-authorization-server-on-sso

创建 lab-68-demo21-authorization-server-on-sso 项目,作为统一登录系统

友情提示:整个实现代码,和我们前文看到的授权服务器是基本一致的。

2.1 初始化数据库

在 resources/db 目录下,有四个 SQL 脚本,分别用于初始化 User 和 OAuth 相关的表。

SQL 脚本

 

2.1.1 初始化 OAuth 表

① 执行 oauth_schema.sql 脚本,创建数据库表结构

drop table if exists oauth_client_details;
create table oauth_client_details (
  client_id VARCHAR(255) PRIMARY KEY,
  resource_ids VARCHAR(255),
  client_secret VARCHAR(255),
  scope VARCHAR(255),
  authorized_grant_types VARCHAR(255),
  web_server_redirect_uri VARCHAR(255),
  authorities VARCHAR(255),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(255)
);

create table if not exists oauth_client_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication_id VARCHAR(255) PRIMARY KEY,
  user_name VARCHAR(255),
  client_id VARCHAR(255)
);

create table if not exists oauth_access_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication_id VARCHAR(255) PRIMARY KEY,
  user_name VARCHAR(255),
  client_id VARCHAR(255),
  authentication LONG VARBINARY,
  refresh_token VARCHAR(255)
);

create table if not exists oauth_refresh_token (
  token_id VARCHAR(255),
  token LONG VARBINARY,
  authentication LONG VARBINARY
);

create table if not exists oauth_code (
  code VARCHAR(255), authentication LONG VARBINARY
);

create table if not exists oauth_approvals (
 userId VARCHAR(255),
 clientId VARCHAR(255),
 scope VARCHAR(255),
 status VARCHAR(10),
 expiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 lastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

结果如下图所示:

表结构

 

作用
oauth_access_token OAuth 2.0 访问令牌
oauth_refresh_token OAuth 2.0 刷新令牌
oauth_code OAuth 2.0 授权码
oauth_client_details OAuth 2.0 客户端
oauth_client_token  
oauth_approvals  

旁白君:这里的表结构设计,我们可以借鉴参考,实现自己的 OAuth 2.0 的功能。

② 执行 oauth_data.sql 脚本,插入一个客户端记录。

INSERT INTO oauth_client_details
 (client_id, client_secret, scope, authorized_grant_types,
 web_server_redirect_uri, authorities, access_token_validity,
 refresh_token_validity, additional_information, autoapprove)
VALUES
 ('clientapp', '112233', 'read_userinfo,read_contacts',
 'password,authorization_code,refresh_token', 'http://127.0.0.1:9090/login', null, 3600, 864000, null, true);

注意!这条记录的 web_server_redirect_uri 字段,我们设置为 http://127.0.0.1:9090/login,这是稍后我们搭建的 XXX 系统的回调地址。

  • 统一登录系统采用 OAuth 2.0 的授权码模式进行授权。

  • 授权成功后,浏览器会跳转 http://127.0.0.1:9090/login 回调地址,然后 XXX 系统会通过授权码向统一登录系统获取访问令牌

通过这样的方式,完成一次单点登录的过程。

结果如下图所示:

oauth_client_details 表记录

 

2.1.2 初始化 User 表

① 执行 user_schema.sql 脚本,创建数据库表结构

DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
  `username` varchar(50) NOT NULL,
  `authority` varchar(50) NOT NULL,
  UNIQUE KEY `ix_auth_username` (`username`,`authority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `username` varchar(50) NOT NULL,
  `password` varchar(500) NOT NULL,
  `enabled` tinyint(1) NOT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

结果如下图所示:

表结构

 

作用
users 用户
authorities 授权表,例如用户拥有的角色

② 执行 user_data.sql 脚本,插入一个用户记录和一个授权记录。

INSERT INTO `authorities` VALUES ('yunai', 'ROLE_USER');

INSERT INTO `users` VALUES ('yunai', '112233', '1');

结果如下图所示:

users 和 authorities 表记录

 

2.2 引入依赖

创建 pom.xml 文件,引入 Spring Security OAuth 依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lab-68</artifactId>
        <groupId>cn.iocoder.springboot.labs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-68-demo21-authorization-server-on-sso</artifactId>

    <properties>
        <!-- 依赖相关配置 -->
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <!-- 插件相关配置 -->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 实现对 Spring MVC 的自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 实现对 Spring Security OAuth2 的自动配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- 实现对数据库连接池的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency> <!-- 本示例,我们使用 MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>

    </dependencies>

</project>

2.3 配置文件

创建 application.yaml 配置文件,添加数据库连接池的配置:

spring:
  # datasource 数据源配置内容,对应 DataSourceProperties 配置属性类
  datasource:
    url: jdbc:mysql://127.0.0.1:43063/demo-68-authorization-server-sso?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root # 数据库账号
    password: 123456 # 数据库密码

2.4 SecurityConfig

创建 SecurityConfig 配置类,通过 Spring Security 提供用户认证的功能。代码如下:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 数据源 DataSource
     */
    @Autowired
    private DataSource dataSource;

    @Override
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource);
    }

}

友情提示:如果胖友想要自定义用户的读取,可以参考《芋道 Spring Boot 安全框架 Spring Security 入门》文章。

2.5 OAuth3AuthorizationServerConfig

创建 OAuth2AuthorizationServerConfig 配置类,通过 Spring Security OAuth 提供授权服务器的功能。代码如下:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 用户认证 Manager
     */
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 数据源 DataSource
     */
    @Autowired
    private DataSource dataSource;

    @Bean
    public TokenStore jdbcTokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .tokenStore(jdbcTokenStore());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess("isAuthenticated()");
    }

    @Bean
    public ClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

}

友情提示:如果胖友看不懂这个配置类,回到《芋道 Spring Security OAuth2 存储器》文章复习下。

2.6 AuthorizationServerApplication

创建 AuthorizationServerApplication 类,统一登录系统的启动类。代码如下:

@SpringBootApplication
public class AuthorizationServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }

}

2.7 简单测试

执行 AuthorizationServerApplication 启动统一登录系统。下面,我们使用 Postman 模拟一个 Client,测试我们是否搭建成功

POST 请求 http://localhost:8080/oauth/token 地址,使用密码模式进行授权。如下图所示:

密码模式

成功获取到访问令牌,成功!

3. 搭建 XXX 系统

示例代码对应仓库:

  • XXX 系统:lab-68-demo21-resource-server-on-sso

创建 lab-68-demo21-resource-server-on-sso 项目,搭建 XXX 系统,接入统一登录系统实现 SSO 功能。

友情提示:整个实现代码,和我们前文看到的资源服务器是基本一致的。

3.1 引入依赖

创建 pom.xml 文件,引入 Spring Security OAuth 依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lab-68</artifactId>
        <groupId>cn.iocoder.springboot.labs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-68-demo21-resource-server</artifactId>

    <properties>
        <!-- 依赖相关配置 -->
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <!-- 插件相关配置 -->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 实现对 Spring MVC 的自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 实现对 Spring Security OAuth2 的自动配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
    </dependencies>

</project>

3.2 配置文件

创建 application.yaml 配置文件,添加 SSO 相关配置:

server:
  port: 9090
  servlet:
    session:
      cookie:
        name: SSO-SESSIONID # 自定义 Session 的 Cookie 名字,防止冲突。冲突后,会导致 SSO 登录失败。

security:
  oauth2:
    # OAuth2 Client 配置,对应 OAuth2ClientProperties 类
    client:
      client-id: clientapp
      client-secret: 112233
      user-authorization-uri: http://127.0.0.1:8080/oauth/authorize # 获取用户的授权码地址
      access-token-uri: http://127.0.0.1:8080/oauth/token # 获取访问令牌的地址
    # OAuth2 Resource 配置,对应 ResourceServerProperties 类
    resource:
      token-info-uri: http://127.0.0.1:8080/oauth/check_token # 校验访问令牌是否有效的地址

① server.servlet.session.cookie.name 配置项,自定义 Session 的 Cookie 名字,防止冲突。冲突后,会导致 SSO 登录失败。

友情提示:具体的值,胖友可以根据自己的喜欢设置。

② security.oauth2.client 配置项,OAuth2 Client 配置,对应 OAuth2ClientProperties 类。在这个配置项中,我们添加了客户端的 client-id 和 client-secret

③ security.oauth2.client.user-authorization-uri 配置项,获取用户的授权码地址。

在访问 XXX 系统需要登录的地址时,Spring Security OAuth 会自动跳转到统一登录系统,进行统一登录获取授权

而这里配置的 security.oauth2.client.user-authorization-uri 地址,就是之前授权服务器的 oauth/authorize 接口,可以进行授权码模式的授权。

友情提示:如果胖友忘记授权服务器的 oauth/authorize 接口,建议回看下《芋道 Spring Security OAuth2 入门》的「3. 授权码模式」小节。

④ security.oauth2.client.access-token-uri 配置项,获取访问令牌的地址。

统一登录系统完成统一登录并授权后,浏览器会跳转回 XXX 系统的回调地址。在该地址上,会调用统一登录系统的 security.oauth2.client.user-authorization-uri 地址,通过授权码获取到访问令牌

而这里配置的 security.oauth2.client.user-authorization-uri 地址,就是之前授权服务器的 oauth/token 接口。

⑤ security.oauth2.resource.client.token-info-uri 配置项,校验访问令牌是否有效的地址。

在获取到访问令牌之后,每次请求 XXX 系统时,都会调用 统一登录系统的 security.oauth2.resource.client.token-info-uri 地址,校验访问令牌的有效性,同时返回用户的基本信息

而这里配置的 security.oauth2.resource.client.token-info-uri 地址,就是之前授权服务器的 oauth/check_token 接口。


至此,我们可以发现,Spring Security OAuth 实现的 SSO 单点登录功能,是基于其授权码模式实现的。这一点,非常重要,稍后我们演示下会更加容易理解到。

3.3 OAuthSsoConfig

创建 OAuthSsoConfig 类,配置接入 SSO 功能。代码如下:

@Configuration
@EnableOAuth2Sso // 开启 Sso 功能
public class OAuthSsoConfig {

}

在类上添加 @EnableOAuth2Sso 注解,声明基于 Spring Security OAuth 的方式接入 SSO 功能。

友情提示:想要深入的胖友,可以看看 SsoSecurityConfigurer 类。

3.4 UserController

创建 UserController 类,提供获取当前用户的 /user/info 接口。代码如下:

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/info")
    public Authentication info(Authentication authentication) {
        return authentication;
    }

}

3.5 ResourceServerApplication

创建 ResourceServerApplication 类,XXX 系统的启动类。代码如下:

@SpringBootApplication
public class ResourceServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ResourceServerApplication.class, args);
    }

}

3.6 简单测试(第一弹)

执行 ResourceServerApplication 启动 XXX 系统。下面,我们来演示下 SSO 单点登录的过程。

① 使用浏览器,访问 XXX 系统的 http://127.0.0.1:9090/user/info 地址。因为暂未登录,所以被重定向到统一登录系统的 http://127.0.0.1:8080/oauth/authorize 授权地址。

又因为在统一登录系统暂未登录,所以被重定向到统一登录系统的 http://127.0.0.1:8080/login 登录地址。如下图所示:

登录界面

② 输入用户的账号密码「yunai/1024」,进行统一登录系统的登录。登录完成后,进入统一登录系统的 http://127.0.0.1:8080/oauth/authorize 授权地址。如下图所示:

授权界面

③ 点击「Authorize」按钮,完成用户的授权。授权完成后,浏览器重定向到 XXX 系统的 http://127.0.0.1:9090/login 回调地址。

在 XX 系统的回调地址,拿到授权的授权码后,会自动请求统一登录系统,通过授权码获取到访问令牌。如此,我们便完成了 XXX 系统 的登录。

获取授权码完成后,自动跳转到登录前的 http://127.0.0.1:9090/user/info 地址,打印出当前登录的用户信息。如下图所示:

用户信息

 


如此,我们从统一登录系统也拿到了用户信息。下面,我们来进一步将 Spring Security 的权限控制功能来演示下。

3.7 SecurityConfig

创建 SecurityConfig 配置类,添加 Spring Security 的功能。代码如下:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启对 Spring Security 注解的方法,进行权限验证。
@Order(101) // OAuth2SsoDefaultConfiguration 使用了 Order(100),所以这里设置为 Order(101),防止相同顺序导致报错
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}

在类上,增加 @EnableGlobalMethodSecurity 注解,开启对 Spring Security 注解的方法,进行权限验证。

3.8 DemoController

创建 DemoController 类,提供测试权限的功能的接口。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

    @GetMapping("/admin-list")
    @PreAuthorize("hasRole('ADMIN')") // 要求管理员 ROLE_ADMIN 角色
    public String adminList() {
        return "管理员列表";
    }

    @GetMapping("/user-list")
    @PreAuthorize("hasRole('USER')") // 要求普通用户 ROLE_USER 角色
    public String userList() {
        return "用户列表";
    }

}

因为当前登录的用户只有 ROLE_USE 角色,所以可以访问 /demo/user-list 接口,无法访问 /demo/admin-list 接口。

3.9 简单测试(第二弹)

执行 ResourceServerApplication 重启 XXX 系统。下面,我们来演示下 Spring Security 的权限控制功能。

① 使用浏览器,访问 http://127.0.0.1:9090/demo/user-list 地址,成功。如下图所示:

成功访问

② 使用浏览器,访问 http://127.0.0.1:9090/demo/admin-list 地址,失败。如下图所示:

失败访问

 

666. 彩蛋

至此,我们成功使用 Spring Security OAuth 实现了一个 SSO 单点登录的示例。下图,是 SSO 的整体流程图,胖友可以继续深入理解下:

SSO 流程图

后续,想要深入的胖友,可以看看 Spring Security OAuth 提供的如下两个过滤器:

  • OAuth2ClientContextFilter

  • OAuth2ClientAuthenticationProcessingFilter

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

写了一个 SSO 单点登录的代码示例给胖友! 的相关文章

  • 【华为OD机试】数字游戏(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 题目描述 小明玩一个游戏 系统发1 n张牌 每张牌上有一个整数 第一张给小明 后n张按照发牌顺序排成连

随机推荐

  • 数字化转型系列主题:数字化建设总体规划蓝图

    本文转自 CIO之家 数字化转型应该是千人千面 因为每家企业的难点痛点不一样 所以每家企业的转型路径都不尽相同 数字化转型不是为了转型而转型 必须是围绕解决企业最大痛点 以它做切入点 回报才最快 投入产出比才最高 所以常想 数字化转型第一件
  • 于仕琪的人脸检测算法

    于仕琪的人脸检测算法 对Windows下的商业使用也免费 刚更新了一次算法 正面人脸检测的角度范围从 40 40 度提升到 60 60 度 检测角度变大但计算量不增加 多视角人脸检测速度提升2倍 速度对比 在同样的条件下OpenCV 47
  • // 计算出给定矩阵中主对角线元素的和

    一 题目 计算出给定矩阵中主对角线元素的和 二 代码 include
  • 数据结构与算法学习总结(六)——字符串的模式匹配算法

    基本概念 字符串是一种特殊的线性表 即元素都是 字符 的线性表 字符是组成字符串的基本单位 字符的取值依赖于字符集 例如二进制的字符集为0 1 则取值只能为 0 1 再比如英语语言 则包括26个字母外加标点符号 例如 abcde 就是一个字
  • python遍历指定目录并打印层级结构

    import os def func filepath n 获取路径 files os listdir filepath for file in files 拼接路径 f d os path join filepath file 判断路径是
  • STM32CubeMX安装与使用

    STM32CubeMX 是 ST 公司近几年来大力推荐的STM32 芯片图形化配置工具 允许用户使用图形化向导生成C 初始化代码 支持多种工具链 比如MDK IAR TrueStudio等 可以大大减轻开发工作时间 提高开发效率 STM32
  • C++ Primer Plus 书之--C++ 模板类深究2--模板类和友元

    模板类和友元 模板类声明也可以有友元 模板的友元分为3类 1 非模板友元 2 约束模板友元 即友元的类型取决于类被实例化时的类型 3 非约束模板友元 即友元的所有具体化都是类的每一个具体化的友元 1 模板类的非模板友元函数 在模板类中将一个
  • 创建型模式,共五种

    设计模式 Design Patterns 可复用面向对象软件的基础 设计模式 Design pattern 是一套被反复使用 多数人知晓的 经过分类编目的 代码设计经验的总结 使用设计模式是为了可重用代码 让代码更容易被他人理解 保证代码可
  • scikit-image 0.17.2计算PSNR、SSIM、MSE

    版本及调用方式 scikit image 0 18 0之前版本的调用方式如下 from skimage measure import compare mse compare psnr compare ssim scikit image 0
  • 数字逻辑练习题(五) 分析下图所示组合逻辑电路的功能

    分析下图所示组合逻辑电路的功能 一 题目描述 分析下图所示组合逻辑电路的功能 要求 1 写出该电路输出 L1 L2 L3 的逻辑函数表达式 2 列出真值表 3 描述该电路的功能 二 题目解答 1 列出逻辑表达式 2 列出真值表 3 电路功能
  • 开发工程中遇到的BUG

    1 Couldn t communicate with a helper application in Xcode 7 问题一 Couldn t communicate with a helper application in Xcode
  • CMake Tutorial Step1

    CMake Tutorial Step1 参考资料 Step 1 A Basic Starting Point CMake 3 26 3 Documentation Tutorial工程 官方Tutorial工程 开发环境 CLion CM
  • 数据库/MySQL - 深入探究 - 1

    1 应用场景 主要用于了解和掌握数据库 MySQL 更新操作详细流程 2 学习 操作 1 文档阅读 主要来自于AI的对话 geek chat chatgpt 以及官方文档资料 以及其他技术文章 专栏等 2 整理输出 抛出问题 数据库 这里以
  • Jenkins部署

    链接 手把手教你用 Jenkins 自动部署 SpringBoot 江南一点雨 jekins 江南一点雨的博客 CSDN博客 bin bash jenkins要在后台执行脚本 需要在脚本前加上BUILD ID dontKillMe BUIL
  • Python while循环结构

    视频版教程 Python3零基础7天入门实战视频教程 循环语句可以在满足循环条件的情况下 反复执行某一段代码 这段被重复执行的代码被称为循环体 当反复执行这个循环体时 需要在合适的时候把循环条件改为假 从而结束循环 否则循环将一直执行下去
  • pycharm语句用法

    Python介绍 Python 是一个高层次的结合了解释性 编译性 互动性和面向对象的脚本语言 Python 的设计具有很强的可读性 相比其他语言经常使用英文关键字 其他语言的一些标点符号 它具有比其他语言更有特色语法结构 Python 是
  • 多种JS代码混淆加密,效果一览。

    演示代码如下 用JShaman对这段代码进行混淆加密 function demo alert hello www jshaman com demo 一 通用版 1 配置选项 压缩代码 保护效果 function demo alert hel
  • SQL数据库的连接、创建操作

    目录 1 数据库的连接 创建 2 对字段的操作 alter table 3 对数据的操作 插入 修改 删除 4 数据查询 select 5 多表查询 join on 6 约束操作 一 数据库的连接 创建 1 连接root数据库 cmd中输入
  • Python爬虫从入门到精通:(8)数据解析_xpath解析基础_Python涛哥

    xpath解析基础 环境安装 pip install lxml 解析原理 html标签是以树状的形式进行展示 实例化一个etree对象 且将待解析的页面源码数据加载到该对象中 调用etree对象的xpath方法结合着不同的xpath表达式实
  • 写了一个 SSO 单点登录的代码示例给胖友!

    发危 摘要 原创出处 http www iocoder cn Spring Security OAuth2 learning sso 芋道源码 欢迎转载 保留摘要 谢谢 1 概述 2 搭建统一登录系统 3 搭建 XXX 系统 666 彩蛋