权限管理系统

权限管理系统

项目概述

业务架构

1581494294533

技术架构

1581494316483

SpringBoot Starter 原理

SpringBoot 简化 Spring 的原因就是提供了许多 starter,而 starter 本质上就是引入了一些相关依赖和进行一些初始化的自动配置。

  • 打包依赖

    spring-boot-starter-web打包相关功能的依赖

  • 自动配置

自动配置原理

  1. 基于 Java 代码的 Bean 配置

    @Configuration + @Bean

  2. 自动配置条件依赖

    @Conditional

  3. Bean 参数获取

    @EnableConfigurationProperties + @ConfigurationProperties(读取配置文件,转换为 Bean 对象)

  4. Bean 的发现

    @EnableAutoConfiguration + @Import

    (解决的是 jar 包中的自动配置类 XXXAutoConfiguration 这些是如何被我们的 @SpringBootApplication 发现的)

  5. Bean 的加载

项目搭建

Nacos 配置

修改 Nacos 的配置文件,将由 Nacos 管理的数据保存到自定义的数据库中,而不是保存到 Nacos 内部的数据库。

  1. 修改 conf/application.properties 文件

    spring.datasource.platform=mysql
    
    ## Count of DB:
    db.num=1
    
    ## Connect URL of DB:
    db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    db.user.0=root
    db.password.0=root
  2. 运行 conf 目录下的 sql 文件

    nacos数据库的表结构

  3. 一般一个项目对应一个 namespace,默认情况下是 public,这里创建一个自定义的命名空间,同时可以在表中查看到相应的变化

    新建命名空间

  4. 在 Nacos 中导入一些和项目相关的配置文件

JWT

不使用签名

    @Test
    public void jwtNoSignTest() {
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "none");
        header.put("type", "JWT");

        Map<String, Object> body = new HashMap<>();
        body.put("userId", "100");
        body.put("role", "admin");

        // 生成JWT Token
        String jwtToken = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .compact();
        System.out.println(jwtToken);

        Jwt jwt = Jwts.parser()
                .parse(jwtToken);

        // Header 和 Body 直接打印输出就是 hash 表
        Header jwtHeader = jwt.getHeader();
        System.out.println("jwtHeader = " + jwtHeader);
        Object alg = jwtHeader.get("alg");
        System.out.println(alg);

        Map<String, Object> jwtBody = (Map<String, Object>) jwt.getBody();
        System.out.println("jwtBody = " + jwtBody);
        Object userId = jwtBody.get("userId");
        System.out.println(userId);
    }

使用 HS256 对称加密算法签名

    @Test
    public void jwtWithSignTest() {
        String secretKey = "HelloWorld";

        Map<String, Object> header = new HashMap<>();
        header.put("alg", SignatureAlgorithm.HS256.getValue());
        header.put("type", "JWT");

        Map<String, Object> body = new HashMap<>();
        body.put("userId", "100");
        body.put("role", "admin");

        String jwtToken = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        System.out.println("jwtToken = " + jwtToken);

        Jwt jwt = Jwts.parser()
                .setSigningKey(secretKey)
                .parse(jwtToken);
        Header jwtHeader = jwt.getHeader();
        Object jwtBody = jwt.getBody();
        System.out.println("jwtHeader = " + jwtHeader);
        System.out.println("jwtBody = " + jwtBody);
    }

使用 RS256 非对称加密算法签名

    private byte[] readBytesFromFile(String filePath) {
        byte[] result;
        InputStream inputStream = null;
        DataInputStream dataInputStream = null;
        try {
            inputStream = ClassLoader.getSystemResourceAsStream(filePath);
            dataInputStream = new DataInputStream(inputStream);
            result = new byte[dataInputStream.available()];
            dataInputStream.readFully(result);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (dataInputStream != null) {
                    dataInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Test
    public void jwtRS256Test() throws Exception {
        //TODO: 这里需要替换成从指定的密钥文件读取的字节
        byte[] privateKeyBytes = readBytesFromFile("pri.key");
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);

        byte[] publicKeyBytes = readBytesFromFile("pub.key");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);

        Map<String, Object> header = new HashMap<>();
        Map<String, Object> body = new HashMap<>();
        String jwtToken = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();

        Jwt jwt = Jwts.parser()
                .setSigningKey(publicKey)
                .parse(jwtToken);
        Header jwtHeader = jwt.getHeader();
        Object jwtBody = jwt.getBody();
        System.out.println("jwtHeader = " + jwtHeader);
        System.out.println("jwtBody = " + jwtBody);
    }

参数解析器

引入 @EnableLoginArgsResolver 注解才开启相应的功能,这种功能的实现是通过在配置类中完成相应功能的设置,但是此时配置类不添加 @Configuration,即该类拥有配置类的功能,但是不被 Spring 框架的组件扫描发现(不被认为是配置类)。而 @EnableLoginArgsResolver 做的事情可以认为是就是 @Import(XXXConfig),即对 @Import 注解的封装,从而让 Spring 框架发现该配置类。

异常处理

如果不进行异常处理,那么程序中如果发生异常,则异常错误会直接显示在页面上,造成不友好的用户体验


   转载规则


《权限管理系统》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Dozer作用:BeanUtils 的升级版,用于实现的对两个 Java Bean 对象之间进行字段的映射,从而进行实体之间的转换。 dozer 映射文件 <?xml version="1.0" encoding="UTF-8"?>
2023-04-07
下一篇 
自定义 Spring-Boot-Starter 自定义 Spring-Boot-Starter
自定义 spring-boot-starterspring-boot-starter 的作用xxx-spring-boot-starter 本质上也是一个 Spring 项目。将其作为一个 pom 依赖引入后,当前项目中可以直接使用在 xx
2023-04-06
  目录