[
  {
    "path": ".gitignore",
    "content": "*/.settings/\n*/target/\n*/.classpath\n*/.project\n\n"
  },
  {
    "path": "01.Start-Spring-Boot/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Start-Spring-Boot</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Start-Spring-Boot</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "01.Start-Spring-Boot/src/main/java/com/springboot/demo/DemoApplication.java",
    "content": "package com.springboot.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@SpringBootApplication\npublic class DemoApplication {\n\n\t@RequestMapping(\"/\")\n\tString index() {\n\t\treturn \"hello spring boot\";\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "01.Start-Spring-Boot/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "01.Start-Spring-Boot/src/test/java/com/springboot/demo/DemoApplicationTests.java",
    "content": "package com.springboot.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Config</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Config</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\n\nimport com.springboot.bean.ConfigBean;\nimport com.springboot.bean.TestConfigBean;\n\n@SpringBootApplication\n@EnableConfigurationProperties({ConfigBean.class,TestConfigBean.class})\n//@ImportResource({\"classpath:some-application.xml\"})\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication app = new SpringApplication(Application.class);\n\t\tapp.setAddCommandLineProperties(false);\n\t\tapp.run(args);\n\t}\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/java/com/springboot/bean/BlogProperties.java",
    "content": "package com.springboot.bean;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BlogProperties {\n\t\n\t@Value(\"${mrbird.blog.name}\")\n\tprivate String name;\n\t\n\t@Value(\"${mrbird.blog.title}\")\n\tprivate String title;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\tpublic void setTitle(String title) {\n\t\tthis.title = title;\n\t}\n\t\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/java/com/springboot/bean/ConfigBean.java",
    "content": "package com.springboot.bean;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n@ConfigurationProperties(prefix=\"mrbird.blog\")\npublic class ConfigBean {\n\tprivate String name;\n\tprivate String title;\n\tprivate String wholeTitle;\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\tpublic void setTitle(String title) {\n\t\tthis.title = title;\n\t}\n\tpublic String getWholeTitle() {\n\t\treturn wholeTitle;\n\t}\n\tpublic void setWholeTitle(String wholeTitle) {\n\t\tthis.wholeTitle = wholeTitle;\n\t}\t\n\t\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/java/com/springboot/bean/TestConfigBean.java",
    "content": "package com.springboot.bean;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.PropertySource;\nimport org.springframework.stereotype.Component;\n\n@Configuration\n@ConfigurationProperties(prefix=\"test\")\n@PropertySource(\"classpath:test.properties\")\n@Component\npublic class TestConfigBean {\n\tprivate String name;\n\tprivate int age;\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic int getAge() {\n\t\treturn age;\n\t}\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\t\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/java/com/springboot/controller/IndexController.java",
    "content": "package com.springboot.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.bean.BlogProperties;\nimport com.springboot.bean.ConfigBean;\nimport com.springboot.bean.TestConfigBean;\n\n\n@RestController\npublic class IndexController {\n\t@Autowired\n\tprivate BlogProperties blogProperties;\n\t@Autowired\n\tprivate ConfigBean configBean;\n\t@Autowired\n\tprivate TestConfigBean testConfigBean;\n\t\n\t@RequestMapping(\"/\")\n\tString index() {\n\t\treturn testConfigBean.getName()+\"，\"+testConfigBean.getAge();\n\t}\n}\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/resources/application-dev.properties",
    "content": "server.port=8080"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/resources/application-prod.properties",
    "content": "server.port=8081"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/resources/application.properties",
    "content": "mrbird.blog.name=mrbird's blog\nmrbird.blog.title=Spring Boot\nmrbird.blog.wholeTitle=${mrbird.blog.name}--${mrbird.blog.title}\n\nspring.profiles.active=dev\n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/resources/banner.txt",
    "content": "  _   _   _   _   _   _  \n / \\ / \\ / \\ / \\ / \\ / \\ \n( m | r | b | i | r | d )\n \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \n"
  },
  {
    "path": "02.Spring-Boot-Config/src/main/resources/test.properties",
    "content": "test.name=KangKang\ntest.age=25"
  },
  {
    "path": "02.Spring-Boot-Config/src/test/java/com/springboot/demo/DemoApplicationTests.java",
    "content": "package com.springboot.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-MyBatis</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/bean/Student.java",
    "content": "package com.springboot.bean;\n\nimport java.io.Serializable;\n\npublic class Student implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -339516038496531943L;\n\tprivate String sno;\n\tprivate String name;\n\tprivate String sex;\n\tpublic String getSno() {\n\t\treturn sno;\n\t}\n\tpublic void setSno(String sno) {\n\t\tthis.sno = sno;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getSex() {\n\t\treturn sex;\n\t}\n\tpublic void setSex(String sex) {\n\t\tthis.sex = sex;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/controller/TestController.java",
    "content": "package com.springboot.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.bean.Student;\nimport com.springboot.service.StudentService;\n\n@RestController\npublic class TestController {\n\n\t@Autowired\n\tprivate StudentService studentService;\n\t\n\t@RequestMapping( value = \"/querystudent\", method = RequestMethod.GET)\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentService.queryStudentBySno(sno);\n\t}\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/mapper/StudentMapper.java",
    "content": "package com.springboot.mapper;\n\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Results;\nimport org.apache.ibatis.annotations.Result;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Component;\n\nimport com.springboot.bean.Student;\n\n@Component\n@Mapper\npublic interface StudentMapper {\n\t@Insert(\"insert into student(sno,sname,ssex) values(#{sno},#{name},#{sex})\")\n\tint add(Student student);\n\t\n\t@Update(\"update student set sname=#{name},ssex=#{sex} where sno=#{sno}\")\n    int update(Student student);\n\t\n\t@Delete(\"delete from student where sno=#{sno}\")\n    int deleteBysno(String sno);\n\t\n\t@Select(\"select * from student where sno=#{sno}\")\n\t@Results(id = \"student\",value= {\n\t\t @Result(property = \"sno\", column = \"sno\", javaType = String.class),\n         @Result(property = \"name\", column = \"sname\", javaType = String.class),\n         @Result(property = \"sex\", column = \"ssex\", javaType = String.class)\n\t})\n    Student queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport com.springboot.bean.Student;\n\npublic interface StudentService {\n\tint add(Student student);\n    int update(Student student);\n    int deleteBysno(String sno);\n    Student queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/java/com/springboot/service/impl/StudentServiceImp.java",
    "content": "package com.springboot.service.impl;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.springboot.bean.Student;\nimport com.springboot.mapper.StudentMapper;\nimport com.springboot.service.StudentService;\n\n@Service(\"studentService\")\npublic class StudentServiceImp implements StudentService{\n\n\t@Autowired\n\tprivate StudentMapper studentMapper;\n\t\n\t@Override\n\tpublic int add(Student student) {\n\t\treturn this.studentMapper.add(student);\n\t}\n\n\t@Override\n\tpublic int update(Student student) {\n\t\treturn this.studentMapper.update(student);\n\t}\n\n\t@Override\n\tpublic int deleteBysno(String sno) {\n\t\treturn this.studentMapper.deleteBysno(sno);\n\t}\n\n\t@Override\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentMapper.queryStudentBySno(sno);\n\t}\n}\n"
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n     "
  },
  {
    "path": "03.Spring-Boot-MyBatis/src/main/resources/init.sql",
    "content": "CREATE TABLE STUDENT (\n    SNO VARCHAR2(3 BYTE) NOT NULL ,\n    SNAME VARCHAR2(9 BYTE) NOT NULL ,\n    SSEX CHAR(2 BYTE) NOT NULL \n);\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ');"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-JdbcTemplate</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-jdbc</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\t\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/bean/Student.java",
    "content": "package com.springboot.bean;\n\nimport java.io.Serializable;\n\npublic class Student implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -339516038496531943L;\n\tprivate String sno;\n\tprivate String name;\n\tprivate String sex;\n\tpublic String getSno() {\n\t\treturn sno;\n\t}\n\tpublic void setSno(String sno) {\n\t\tthis.sno = sno;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getSex() {\n\t\treturn sex;\n\t}\n\tpublic void setSex(String sex) {\n\t\tthis.sex = sex;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/controller/TestController.java",
    "content": "package com.springboot.controller;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.bean.Student;\nimport com.springboot.service.StudentService;\n\n@RestController\npublic class TestController {\n\n\t@Autowired\n\tprivate StudentService studentService;\n\n\t@RequestMapping(value = \"/querystudent\", method = RequestMethod.GET)\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentService.queryStudentBySno(sno);\n\t}\n\n\t@RequestMapping(value = \"/queryallstudent\")\n\tpublic List<Map<String, Object>> queryAllStudent() {\n\t\treturn this.studentService.queryStudentListMap();\n\t}\n\t\n\t@RequestMapping(value = \"/addstudent\", method = RequestMethod.GET)\n\tpublic int saveStudent(String sno,String name,String sex) {\n\t\tStudent student = new Student();\n\t\tstudent.setSno(sno);\n\t\tstudent.setName(name);\n\t\tstudent.setSex(sex);\n\t\treturn this.studentService.add(student);\n\t}\n\t\n\t@RequestMapping(value = \"deletestudent\", method = RequestMethod.GET)\n\tpublic int deleteStudentBySno(String sno) {\n\t\treturn this.studentService.deleteBysno(sno);\n\t}\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/dao/StudentDao.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.springboot.bean.Student;\n\npublic interface StudentDao {\n\tint add(Student student);\n    int update(Student student);\n    int deleteBysno(String sno);\n    List<Map<String,Object>> queryStudentsListMap();\n    Student queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/dao/impl/StudentDaoImp.java",
    "content": "package com.springboot.dao.impl;\n\nimport java.sql.Types;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.bean.Student;\nimport com.springboot.dao.StudentDao;\nimport com.springboot.mapper.StudentMapper;\n\n@Repository(\"studentDao\")\npublic class StudentDaoImp implements StudentDao {\n\n\t@Autowired\n\tprivate JdbcTemplate jdbcTemplate;\n\n\t@Override\n\tpublic int add(Student student) {\n\t\t// String sql = \"insert into student(sno,sname,ssex) values(?,?,?)\";\n\t\t// Object[] args = { student.getSno(), student.getName(), student.getSex() };\n\t\t// int[] argTypes = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR };\n\t\t// return this.jdbcTemplate.update(sql, args, argTypes);\n\t\tString sql = \"insert into student(sno,sname,ssex) values(:sno,:name,:sex)\";\n\t\tNamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(this.jdbcTemplate.getDataSource());\n\t\treturn npjt.update(sql, new BeanPropertySqlParameterSource(student));\n\t}\n\n\t@Override\n\tpublic int update(Student student) {\n\t\tString sql = \"update student set sname = ?,ssex = ? where sno = ?\";\n\t\tObject[] args = { student.getName(), student.getSex(), student.getSno() };\n\t\tint[] argTypes = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR };\n\t\treturn this.jdbcTemplate.update(sql, args, argTypes);\n\t}\n\n\t@Override\n\tpublic int deleteBysno(String sno) {\n\t\tString sql = \"delete from student where sno = ?\";\n\t\tObject[] args = { sno };\n\t\tint[] argTypes = { Types.VARCHAR };\n\t\treturn this.jdbcTemplate.update(sql, args, argTypes);\n\t}\n\n\t@Override\n\tpublic List<Map<String, Object>> queryStudentsListMap() {\n\t\tString sql = \"select * from student\";\n\t\treturn this.jdbcTemplate.queryForList(sql);\n\t}\n\n\t@Override\n\tpublic Student queryStudentBySno(String sno) {\n\t\tString sql = \"select * from student where sno = ?\";\n\t\tObject[] args = { sno };\n\t\tint[] argTypes = { Types.VARCHAR };\n\t\tList<Student> studentList = this.jdbcTemplate.query(sql, args, argTypes, new StudentMapper());\n\t\tif (studentList != null && studentList.size() > 0) {\n\t\t\treturn studentList.get(0);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/mapper/StudentMapper.java",
    "content": "package com.springboot.mapper;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport org.springframework.jdbc.core.RowMapper;\n\nimport com.springboot.bean.Student;\n\npublic class StudentMapper implements RowMapper<Student>{\n\n\t@Override\n\tpublic Student mapRow(ResultSet rs, int rowNum) throws SQLException {\n\t\tStudent student = new Student();\n\t\tstudent.setSno(rs.getString(\"sno\"));\n\t\tstudent.setName(rs.getString(\"sname\"));\n\t\tstudent.setSex(rs.getString(\"ssex\"));\n\t\treturn student;\n\t}\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.springboot.bean.Student;\n\npublic interface StudentService {\n\tint add(Student student);\n    int update(Student student);\n    int deleteBysno(String sno);\n    List<Map<String, Object>> queryStudentListMap();\n    Student queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/java/com/springboot/service/impl/StudentServiceImp.java",
    "content": "package com.springboot.service.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.springboot.bean.Student;\nimport com.springboot.dao.StudentDao;\nimport com.springboot.mapper.StudentMapper;\nimport com.springboot.service.StudentService;\n\n@Service(\"studentService\")\npublic class StudentServiceImp implements StudentService {\n\n\t@Autowired\n\tprivate StudentDao studentDao;\n\n\t@Override\n\tpublic int add(Student student) {\n\t\treturn this.studentDao.add(student);\n\t}\n\n\t@Override\n\tpublic int update(Student student) {\n\t\treturn this.studentDao.update(student);\n\t}\n\n\t@Override\n\tpublic int deleteBysno(String sno) {\n\t\treturn this.studentDao.deleteBysno(sno);\n\t}\n\n\t@Override\n\tpublic List<Map<String, Object>> queryStudentListMap() {\n\t\treturn this.studentDao.queryStudentsListMap();\n\t}\n\n\t@Override\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentDao.queryStudentBySno(sno);\n\t}\n\n}\n"
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n     "
  },
  {
    "path": "04.Spring-Boot-JdbcTemplate/src/main/resources/init.sql",
    "content": "CREATE TABLE STUDENT (\n    SNO VARCHAR2(3 BYTE) NOT NULL ,\n    SNAME VARCHAR2(9 BYTE) NOT NULL ,\n    SSEX CHAR(2 BYTE) NOT NULL \n);\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ');"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-MyBatis-MultiDataSource</artifactId>\n\t<version>1.0-snapshot</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- mysql驱动 -->\n\t\t<dependency>\n\t\t    <groupId>mysql</groupId>\n\t\t    <artifactId>mysql-connector-java</artifactId>\n\t\t</dependency>\n\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/controller/StudentController.java",
    "content": "package com.springboot.controller;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.service.StudentService;\n\n@RestController\npublic class StudentController {\n\t\n\t@Autowired\n\tprivate StudentService studentService;\n\t\n\t@RequestMapping(\"querystudentsfromoracle\")\n\tpublic List<Map<String, Object>> queryStudentsFromOracle(){\n\t\treturn this.studentService.getAllStudentsFromOralce();\n\t}\n\t\n\t@RequestMapping(\"querystudentsfrommysql\")\n\tpublic List<Map<String, Object>> queryStudentsFromMysql(){\n\t\treturn this.studentService.getAllStudentsFromMysql();\n\t}\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/datasource/MysqlDatasourceConfig.java",
    "content": "package com.springboot.datasource;\n\nimport javax.sql.DataSource;\n\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\n\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;\n\n@Configuration\n@MapperScan(basePackages = MysqlDatasourceConfig.PACKAGE, sqlSessionFactoryRef = \"mysqlSqlSessionFactory\")\npublic class MysqlDatasourceConfig {\n\n\t// mysqldao扫描路径\n\tstatic final String PACKAGE = \"com.springboot.mysqldao\";\n\t// mybatis mapper扫描路径\n\tstatic final String MAPPER_LOCATION = \"classpath:mapper/mysql/*.xml\";\n\n\t@Primary\n\t@Bean(name = \"mysqldatasource\")\n\t@ConfigurationProperties(\"spring.datasource.druid.mysql\")\n\tpublic DataSource mysqlDataSource() {\n\t\treturn DruidDataSourceBuilder.create().build();\n\t}\n\n\t@Bean(name = \"mysqlTransactionManager\")\n\t@Primary\n\tpublic DataSourceTransactionManager mysqlTransactionManager() {\n\t\treturn new DataSourceTransactionManager(mysqlDataSource());\n\t}\n\n\t@Bean(name = \"mysqlSqlSessionFactory\")\n\t@Primary\n\tpublic SqlSessionFactory mysqlSqlSessionFactory(@Qualifier(\"mysqldatasource\") DataSource dataSource)\n\t\t\tthrows Exception {\n\t\tfinal SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();\n\t\tsessionFactory.setDataSource(dataSource);\n\t\t//如果不使用xml的方式配置mapper，则可以省去下面这行mapper location的配置。\n\t\tsessionFactory.setMapperLocations(\n\t\t\t\tnew PathMatchingResourcePatternResolver().getResources(MysqlDatasourceConfig.MAPPER_LOCATION));\n\t\treturn sessionFactory.getObject();\n\t}\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/datasource/OracleDatasourceConfig.java",
    "content": "package com.springboot.datasource;\n\nimport javax.sql.DataSource;\n\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\n\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;\n\n@Configuration\n@MapperScan(basePackages = OracleDatasourceConfig.PACKAGE, \n\tsqlSessionFactoryRef = \"oracleSqlSessionFactory\")\npublic class OracleDatasourceConfig {\n\t\n\t// oracledao扫描路径\n\tstatic final String PACKAGE = \"com.springboot.oracledao\"; \n\t// mybatis mapper扫描路径\n\tstatic final String MAPPER_LOCATION = \"classpath:mapper/oracle/*.xml\";\n\t\n\t@Bean(name = \"oracledatasource\")\n\t@ConfigurationProperties(\"spring.datasource.druid.oracle\")\n\tpublic DataSource oracleDataSource() {\n\t\treturn DruidDataSourceBuilder.create().build();\n\t}\n\t\n\t@Bean(name = \"oracleTransactionManager\")\n    public DataSourceTransactionManager oracleTransactionManager() {\n        return new DataSourceTransactionManager(oracleDataSource());\n    }\n \n    @Bean(name = \"oracleSqlSessionFactory\")\n    public SqlSessionFactory oracleSqlSessionFactory(@Qualifier(\"oracledatasource\") DataSource dataSource) throws Exception {\n        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();\n        sessionFactory.setDataSource(dataSource);\n        //如果不使用xml的方式配置mapper，则可以省去下面这行mapper location的配置。\n        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()\n                .getResources(OracleDatasourceConfig.MAPPER_LOCATION));\n        return sessionFactory.getObject();\n    }\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/mysqldao/MysqlStudentMapper.java",
    "content": "package com.springboot.mysqldao;\n\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface MysqlStudentMapper {\n\tList<Map<String, Object>> getAllStudents();\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/oracledao/OracleStudentMapper.java",
    "content": "package com.springboot.oracledao;\n\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface OracleStudentMapper {\n\tList<Map<String, Object>> getAllStudents();\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface StudentService {\n\tList<Map<String, Object>> getAllStudentsFromOralce();\n\tList<Map<String, Object>> getAllStudentsFromMysql();\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/java/com/springboot/service/impl/StudentServiceImp.java",
    "content": "package com.springboot.service.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.springboot.mysqldao.MysqlStudentMapper;\nimport com.springboot.oracledao.OracleStudentMapper;\nimport com.springboot.service.StudentService;\n\n@Service(\"studentService\")\npublic class StudentServiceImp implements StudentService{\n\t@Autowired\n\tprivate OracleStudentMapper oracleStudentMapper;\n\t@Autowired\n\tprivate MysqlStudentMapper mysqlStudentMapper;\n\t\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudentsFromOralce() {\n\t\treturn this.oracleStudentMapper.getAllStudents();\n\t}\n\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudentsFromMysql() {\n\t\treturn this.mysqlStudentMapper.getAllStudents();\n\t}\n\n}\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      # 数据源1 mysql\n      mysql:\n        type: com.alibaba.druid.pool.DruidDataSource\n        driver-class-name: com.mysql.jdbc.Driver\n        url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull\n        username: root\n        password: 123456\n      # 数据源2 oracle\n      oracle: \n        type: com.alibaba.druid.pool.DruidDataSource\n        driver-class-name: oracle.jdbc.driver.OracleDriver\n        url: jdbc:oracle:thin:@localhost:1521:ORCL\n        username: test\n        password: 123456\n        \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n\n     "
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/resources/mapper/mysql/MysqlStudentMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>    \n    <!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"   \n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">     \n<mapper namespace=\"com.springboot.mysqldao.MysqlStudentMapper\">  \n    <select id=\"getAllStudents\" resultType=\"java.util.Map\">\n        select * from student\n    </select>\n</mapper>"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/resources/mapper/oracle/OracleStudentMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>    \n    <!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"   \n\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">     \n<mapper namespace=\"com.springboot.oracledao.OracleStudentMapper\">  \n    <select id=\"getAllStudents\" resultType=\"java.util.Map\">\n        select * from student\n    </select>\n</mapper>"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/resources/mysql_sql.sql",
    "content": "/*\nNavicat MySQL Data Transfer\n\nSource Server         : localhost_3306\nSource Server Version : 50717\nSource Host           : localhost:3306\nSource Database       : test\n\nTarget Server Type    : MYSQL\nTarget Server Version : 50717\nFile Encoding         : 65001\n\nDate: 2018-05-02 14:32:22\n*/\n\nSET FOREIGN_KEY_CHECKS=0;\n\n-- ----------------------------\n-- Table structure for student\n-- ----------------------------\nDROP TABLE IF EXISTS `student`;\nCREATE TABLE `student` (\n  `SNO` varchar(3) NOT NULL,\n  `SNAME` varchar(10) NOT NULL,\n  `SSEX` char(2) NOT NULL,\n  `DATASOURCE` varchar(10) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n-- Records of student\n-- ----------------------------\nINSERT INTO `student` VALUES ('001', 'KangKang', 'M', 'mysql');\nINSERT INTO `student` VALUES ('002', 'Mike', 'M', 'mysql');\n"
  },
  {
    "path": "05.Spring-Boot-MyBatis-MultiDataSource/src/main/resources/oracle_sql.sql",
    "content": "/*\nNavicat Oracle Data Transfer\nOracle Client Version : 11.2.0.1.0\n\nSource Server         : localhost_test\nSource Server Version : 110200\nSource Host           : localhost:1521\nSource Schema         : TEST\n\nTarget Server Type    : ORACLE\nTarget Server Version : 110200\nFile Encoding         : 65001\n\nDate: 2018-05-02 14:31:18\n*/\n\n\n-- ----------------------------\n-- Table structure for STUDENT\n-- ----------------------------\nDROP TABLE STUDENT;\nCREATE TABLE STUDENT (\nSNO VARCHAR2(3 BYTE) NOT NULL ,\nSNAME VARCHAR2(9 BYTE) NOT NULL ,\nSSEX CHAR(2 BYTE) NOT NULL ,\nDATASOURCE VARCHAR2(10 BYTE) NULL \n)\nLOGGING\nNOCOMPRESS\nNOCACHE\n\n;\n\n-- ----------------------------\n-- Records of STUDENT\n-- ----------------------------\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ', 'oracle');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ', 'oracle');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ', 'oracle');\nINSERT INTO STUDENT VALUES ('004', 'Maria', 'F ', 'oracle');\n\n-- ----------------------------\n-- Checks structure for table STUDENT\n-- ----------------------------\nALTER TABLE STUDENT ADD CHECK (SNO IS NOT NULL);\nALTER TABLE STUDENT ADD CHECK (SNAME IS NOT NULL);\nALTER TABLE STUDENT ADD CHECK (SSEX IS NOT NULL);\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-JdbcTemplate-MultiDataSource</artifactId>\n\t<version>1.0-snapshot</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- mysql驱动 -->\n\t\t<dependency>\n\t\t    <groupId>mysql</groupId>\n\t\t    <artifactId>mysql-connector-java</artifactId>\n\t\t</dependency>\n\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/controller/StudentController.java",
    "content": "package com.springboot.controller;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.service.StudentService;\n\n@RestController\npublic class StudentController {\n\t\n\t@Autowired\n\tprivate StudentService studentService;\n\t\n\t@RequestMapping(\"querystudentsfromoracle\")\n\tpublic List<Map<String, Object>> queryStudentsFromOracle(){\n\t\treturn this.studentService.getAllStudentsFromOralce();\n\t}\n\t\n\t@RequestMapping(\"querystudentsfrommysql\")\n\tpublic List<Map<String, Object>> queryStudentsFromMysql(){\n\t\treturn this.studentService.getAllStudentsFromMysql();\n\t}\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/dao/MysqlStudentDao.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport java.util.Map;\n\n\npublic interface MysqlStudentDao {\n\tList<Map<String, Object>> getAllStudents();\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/dao/OracleStudentDao.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface OracleStudentDao {\n\tList<Map<String, Object>> getAllStudents();\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/dao/impl/MysqlStudentDaoImp.java",
    "content": "package com.springboot.dao.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.dao.MysqlStudentDao;\n\n@Repository\npublic class MysqlStudentDaoImp implements MysqlStudentDao{\n\t\n\t@Autowired\n\t@Qualifier(\"mysqlJdbcTemplate\")\n\tprivate JdbcTemplate jdbcTemplate;\n\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudents() {\n\t\treturn this.jdbcTemplate.queryForList(\"select * from student\");\n\t}\n\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/dao/impl/OracleStudentDaoImp.java",
    "content": "package com.springboot.dao.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.dao.OracleStudentDao;\n\n@Repository\npublic class OracleStudentDaoImp implements OracleStudentDao{\n\n\t@Autowired\n\t@Qualifier(\"oracleJdbcTemplate\")\n\tprivate JdbcTemplate jdbcTemplate;\n\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudents() {\n\t\treturn this.jdbcTemplate.queryForList(\"select * from student\");\n\t}\n\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/datasource/DataSourceConfig.java",
    "content": "package com.springboot.datasource;\n\nimport javax.sql.DataSource;\n\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;\n\n@Configuration\npublic class DataSourceConfig {\n\t@Primary\n\t@Bean(name = \"mysqldatasource\")\n\t@ConfigurationProperties(\"spring.datasource.druid.mysql\")\n\tpublic DataSource dataSourceOne(){\n\t    return DruidDataSourceBuilder.create().build();\n\t}\n\t\n\t@Bean(name = \"oracledatasource\")\n\t@ConfigurationProperties(\"spring.datasource.druid.oracle\")\n\tpublic DataSource dataSourceTwo(){\n\t    return DruidDataSourceBuilder.create().build();\n\t}\n\n\t@Bean(name = \"mysqlJdbcTemplate\")\n\tpublic JdbcTemplate primaryJdbcTemplate(\n\t        @Qualifier(\"mysqldatasource\") DataSource dataSource) {\n\t    return new JdbcTemplate(dataSource);\n\t}\n\t\n\t@Bean(name = \"oracleJdbcTemplate\")\n\tpublic JdbcTemplate secondaryJdbcTemplate(\n\t        @Qualifier(\"oracledatasource\") DataSource dataSource) {\n\t    return new JdbcTemplate(dataSource);\n\t}\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic interface StudentService {\n\tList<Map<String, Object>> getAllStudentsFromOralce();\n\tList<Map<String, Object>> getAllStudentsFromMysql();\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/java/com/springboot/service/impl/StudentServiceImp.java",
    "content": "package com.springboot.service.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.springboot.dao.MysqlStudentDao;\nimport com.springboot.dao.OracleStudentDao;\nimport com.springboot.service.StudentService;\n\n@Service(\"studentService\")\npublic class StudentServiceImp implements StudentService{\n\t@Autowired\n\tprivate OracleStudentDao oracleStudentDao;\n\t@Autowired\n\tprivate MysqlStudentDao mysqlStudentDao;\n\t\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudentsFromOralce() {\n\t\treturn this.oracleStudentDao.getAllStudents();\n\t}\n\n\t@Override\n\tpublic List<Map<String, Object>> getAllStudentsFromMysql() {\n\t\treturn this.mysqlStudentDao.getAllStudents();\n\t}\n\n}\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      # 数据源1 mysql\n      mysql:\n        type: com.alibaba.druid.pool.DruidDataSource\n        driver-class-name: com.mysql.jdbc.Driver\n        url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull\n        username: root\n        password: 123456\n      # 数据源2 oracle\n      oracle: \n        type: com.alibaba.druid.pool.DruidDataSource\n        driver-class-name: oracle.jdbc.driver.OracleDriver\n        url: jdbc:oracle:thin:@localhost:1521:ORCL\n        username: test\n        password: 123456\n        \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n     "
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/resources/mysql_sql.sql",
    "content": "/*\nNavicat MySQL Data Transfer\n\nSource Server         : localhost_3306\nSource Server Version : 50717\nSource Host           : localhost:3306\nSource Database       : test\n\nTarget Server Type    : MYSQL\nTarget Server Version : 50717\nFile Encoding         : 65001\n\nDate: 2018-05-02 14:32:22\n*/\n\nSET FOREIGN_KEY_CHECKS=0;\n\n-- ----------------------------\n-- Table structure for student\n-- ----------------------------\nDROP TABLE IF EXISTS `student`;\nCREATE TABLE `student` (\n  `SNO` varchar(3) NOT NULL,\n  `SNAME` varchar(10) NOT NULL,\n  `SSEX` char(2) NOT NULL,\n  `DATASOURCE` varchar(10) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n-- Records of student\n-- ----------------------------\nINSERT INTO `student` VALUES ('001', 'KangKang', 'M', 'mysql');\nINSERT INTO `student` VALUES ('002', 'Mike', 'M', 'mysql');\n"
  },
  {
    "path": "06.Spring-Boot-JdbcTemplate-MultiDataSource/src/main/resources/oracle_sql.sql",
    "content": "/*\nNavicat Oracle Data Transfer\nOracle Client Version : 11.2.0.1.0\n\nSource Server         : localhost_test\nSource Server Version : 110200\nSource Host           : localhost:1521\nSource Schema         : TEST\n\nTarget Server Type    : ORACLE\nTarget Server Version : 110200\nFile Encoding         : 65001\n\nDate: 2018-05-02 14:31:18\n*/\n\n\n-- ----------------------------\n-- Table structure for STUDENT\n-- ----------------------------\nDROP TABLE STUDENT;\nCREATE TABLE STUDENT (\nSNO VARCHAR2(3 BYTE) NOT NULL ,\nSNAME VARCHAR2(9 BYTE) NOT NULL ,\nSSEX CHAR(2 BYTE) NOT NULL ,\nDATASOURCE VARCHAR2(10 BYTE) NULL \n)\nLOGGING\nNOCOMPRESS\nNOCACHE\n\n;\n\n-- ----------------------------\n-- Records of STUDENT\n-- ----------------------------\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ', 'oracle');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ', 'oracle');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ', 'oracle');\nINSERT INTO STUDENT VALUES ('004', 'Maria', 'F ', 'oracle');\n\n-- ----------------------------\n-- Checks structure for table STUDENT\n-- ----------------------------\nALTER TABLE STUDENT ADD CHECK (SNO IS NOT NULL);\nALTER TABLE STUDENT ADD CHECK (SNAME IS NOT NULL);\nALTER TABLE STUDENT ADD CHECK (SSEX IS NOT NULL);\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-AOP-Log</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-jdbc</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- aop依赖 -->\n\t\t<dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/annotation/Log.java",
    "content": "package com.springboot.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Log {\n\tString value() default \"\";\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/aspect/LogAspect.java",
    "content": "package com.springboot.aspect;\n\nimport java.lang.reflect.Method;\nimport java.util.Date;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\nimport org.aspectj.lang.reflect.MethodSignature;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.LocalVariableTableParameterNameDiscoverer;\nimport org.springframework.stereotype.Component;\n\nimport com.springboot.annotation.Log;\nimport com.springboot.dao.SysLogDao;\nimport com.springboot.domain.SysLog;\nimport com.springboot.util.HttpContextUtils;\nimport com.springboot.util.IPUtils;\n\n@Aspect\n@Component\npublic class LogAspect {\n\n\t@Autowired\n\tprivate SysLogDao sysLogDao;\n\n\t@Pointcut(\"@annotation(com.springboot.annotation.Log)\")\n\tpublic void pointcut() {\n\t}\n\n\t@Around(\"pointcut()\")\n\tpublic void around(ProceedingJoinPoint point) {\n\t\tlong beginTime = System.currentTimeMillis();\n\t\ttry {\n\t\t\t// 执行方法\n\t\t\tpoint.proceed();\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t// 执行时长(毫秒)\n\t\tlong time = System.currentTimeMillis() - beginTime;\n\t\t// 保存日志\n\t\tsaveLog(point, time);\n\t}\n\n\tprivate void saveLog(ProceedingJoinPoint joinPoint, long time) {\n\t\tMethodSignature signature = (MethodSignature) joinPoint.getSignature();\n\t\tMethod method = signature.getMethod();\n\t\tSysLog sysLog = new SysLog();\n\t\tLog logAnnotation = method.getAnnotation(Log.class);\n\t\tif (logAnnotation != null) {\n\t\t\t// 注解上的描述\n\t\t\tsysLog.setOperation(logAnnotation.value());\n\t\t}\n\t\t// 请求的方法名\n\t\tString className = joinPoint.getTarget().getClass().getName();\n\t\tString methodName = signature.getName();\n\t\tsysLog.setMethod(className + \".\" + methodName + \"()\");\n\t\t// 请求的方法参数值\n\t\tObject[] args = joinPoint.getArgs();\n\t\t// 请求的方法参数名称\n\t\tLocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();\n\t\tString[] paramNames = u.getParameterNames(method);\n\t\tif (args != null && paramNames != null) {\n\t\t\tString params = \"\";\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tparams += \"  \" + paramNames[i] + \": \" + args[i];\n\t\t\t}\n\t\t\tsysLog.setParams(params);\n\t\t}\n\t\t// 获取request\n\t\tHttpServletRequest request = HttpContextUtils.getHttpServletRequest();\n\t\t// 设置IP地址\n\t\tsysLog.setIp(IPUtils.getIpAddr(request));\n\t\t// 模拟一个用户名\n\t\tsysLog.setUsername(\"mrbird\");\n\t\tsysLog.setTime((int) time);\n\t\tDate date = new Date();\n\t\tsysLog.setCreateTime(date);\n\t\t// 保存系统日志\n\t\tsysLogDao.saveSysLog(sysLog);\n\t}\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/controller/TestController.java",
    "content": "package com.springboot.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport com.springboot.annotation.Log;\n\n@RestController\npublic class TestController {\n\n\t@Log(\"执行方法一\")\n\t@GetMapping(\"/one\")\n\tpublic void methodOne(String name) {\n\t\t\n\t}\n\n\t@Log(\"执行方法二\")\n\t@GetMapping(\"/two\")\n\tpublic void methodTwo() throws InterruptedException {\n\t\tThread.sleep(2000);\n\t}\n\n\t@Log(\"执行方法三\")\n\t@GetMapping(\"/three\")\n\tpublic void methodThree(String name, String age) {\n\t\t\n\t}\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/dao/SysLogDao.java",
    "content": "package com.springboot.dao;\n\nimport com.springboot.domain.SysLog;\n\npublic interface SysLogDao {\n\tvoid saveSysLog(SysLog syslog);\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/dao/impl/SysLogDaoImp.java",
    "content": "package com.springboot.dao.impl;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.dao.SysLogDao;\nimport com.springboot.domain.SysLog;\n\n@Repository\npublic class SysLogDaoImp implements SysLogDao {\n\n\t@Autowired\n\tprivate JdbcTemplate jdbcTemplate;\n\n\t@Override\n\tpublic void saveSysLog(SysLog syslog) {\n\t\tStringBuffer sql = new StringBuffer(\"insert into sys_log \");\n\t\tsql.append(\"(id,username,operation,time,method,params,ip,create_time) \");\n\t\tsql.append(\"values(seq_sys_log.nextval,:username,:operation,:time,:method,\");\n\t\tsql.append(\":params,:ip,:createTime)\");\n\n\t\tNamedParameterJdbcTemplate npjt = new NamedParameterJdbcTemplate(this.jdbcTemplate.getDataSource());\n\t\tnpjt.update(sql.toString(), new BeanPropertySqlParameterSource(syslog));\n\t}\n\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/domain/SysLog.java",
    "content": "package com.springboot.domain;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class SysLog implements Serializable{\n\n\tprivate static final long serialVersionUID = -6309732882044872298L;\n\t\n\tprivate Integer id;\n\tprivate String username;\n\tprivate String operation;\n\tprivate Integer time;\n\tprivate String method;\n\tprivate String params;\n\tprivate String ip;\n\tprivate Date createTime;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUsername() {\n\t\treturn username;\n\t}\n\tpublic void setUsername(String username) {\n\t\tthis.username = username;\n\t}\n\tpublic String getOperation() {\n\t\treturn operation;\n\t}\n\tpublic void setOperation(String operation) {\n\t\tthis.operation = operation;\n\t}\n\tpublic Integer getTime() {\n\t\treturn time;\n\t}\n\tpublic void setTime(Integer time) {\n\t\tthis.time = time;\n\t}\n\tpublic String getMethod() {\n\t\treturn method;\n\t}\n\tpublic void setMethod(String method) {\n\t\tthis.method = method;\n\t}\n\tpublic String getParams() {\n\t\treturn params;\n\t}\n\tpublic void setParams(String params) {\n\t\tthis.params = params;\n\t}\n\tpublic String getIp() {\n\t\treturn ip;\n\t}\n\tpublic void setIp(String ip) {\n\t\tthis.ip = ip;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\t\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/util/HttpContextUtils.java",
    "content": "package com.springboot.util;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\npublic class HttpContextUtils {\n\tpublic static HttpServletRequest getHttpServletRequest() {\n\t\treturn ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();\n\t}\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/java/com/springboot/util/IPUtils.java",
    "content": "package com.springboot.util;\n\nimport javax.servlet.http.HttpServletRequest;\n\npublic class IPUtils {\n\n\t/**\n\t * 获取IP地址\n\t * \n\t * 使用Nginx等反向代理软件， 则不能通过request.getRemoteAddr()获取IP地址\n\t * 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，X-Forwarded-For中第一个非unknown的有效IP字符串，则为真实IP地址\n\t */\n\tpublic static String getIpAddr(HttpServletRequest request) {\n\n\t\tString ip = request.getHeader(\"x-forwarded-for\");\n\t\tif (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n\t\t\tip = request.getHeader(\"Proxy-Client-IP\");\n\t\t}\n\t\tif (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n\t\t\tip = request.getHeader(\"WL-Proxy-Client-IP\");\n\t\t}\n\t\tif (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n\t\t\tip = request.getRemoteAddr();\n\t\t}\n\t\treturn \"0:0:0:0:0:0:0:1\".equals(ip) ? \"127.0.0.1\" : ip;\n\t}\n\n}\n"
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n\n     "
  },
  {
    "path": "07.Spring-Boot-AOP-Log/src/main/resources/init.sql",
    "content": "CREATE TABLE SYS_LOG (\n   ID NUMBER(20) NOT NULL ,\n   USERNAME VARCHAR2(50 BYTE) NULL ,\n   OPERATION VARCHAR2(50 BYTE) NULL ,\n   TIME NUMBER(11) NULL ,\n   METHOD VARCHAR2(200 BYTE) NULL ,\n   PARAMS VARCHAR2(500 BYTE) NULL ,\n   IP VARCHAR2(64 BYTE) NULL ,\n   CREATE_TIME DATE NULL \n);\nCOMMENT ON COLUMN SYS_LOG.USERNAME IS '用户名';\nCOMMENT ON COLUMN SYS_LOG.OPERATION IS '用户操作';\nCOMMENT ON COLUMN SYS_LOG.TIME IS '响应时间';\nCOMMENT ON COLUMN SYS_LOG.METHOD IS '请求方法';\nCOMMENT ON COLUMN SYS_LOG.PARAMS IS '请求参数';\nCOMMENT ON COLUMN SYS_LOG.IP IS 'IP地址';\nCOMMENT ON COLUMN SYS_LOG.CREATE_TIME IS '创建时间';\nCREATE SEQUENCE seq_sys_log START WITH 1 INCREMENT BY 1;"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Thymeleaf</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n \t\t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/java/com/springboot/bean/Account.java",
    "content": "package com.springboot.bean;\n\npublic class Account {\n\tprivate String account;\n\tprivate String name;\n\tprivate String password;\n\tprivate String accountType;\n\tprivate String tel;\n\t\n\tpublic Account(String account, String name, String password, String accountType, String tel) {\n\t\tsuper();\n\t\tthis.account = account;\n\t\tthis.name = name;\n\t\tthis.password = password;\n\t\tthis.accountType = accountType;\n\t\tthis.tel = tel;\n\t}\n\tpublic String getAccount() {\n\t\treturn account;\n\t}\n\tpublic void setAccount(String account) {\n\t\tthis.account = account;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic String getAccountType() {\n\t\treturn accountType;\n\t}\n\tpublic void setAccountType(String accountType) {\n\t\tthis.accountType = accountType;\n\t}\n\tpublic String getTel() {\n\t\treturn tel;\n\t}\n\tpublic void setTel(String tel) {\n\t\tthis.tel = tel;\n\t}\n\t\n}\n"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/java/com/springboot/controller/IndexController.java",
    "content": "package com.springboot.controller;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\nimport com.springboot.bean.Account;\n\n@Controller\npublic class IndexController {\n\t\n\t@RequestMapping(\"/account\")\n\tpublic String index(Model m) {\n\t\tList<Account> list = new ArrayList<Account>();\n\t\tlist.add(new Account(\"KangKang\", \"康康\", \"e10adc3949ba59abbe56e\", \"超级管理员\", \"17777777777\"));\n\t\tlist.add(new Account(\"Mike\", \"麦克\", \"e10adc3949ba59abbe56e\", \"管理员\", \"13444444444\"));\n\t\tlist.add(new Account(\"Jane\",\"简\",\"e10adc3949ba59abbe56e\",\"运维人员\",\"18666666666\"));\n\t\tlist.add(new Account(\"Maria\", \"玛利亚\", \"e10adc3949ba59abbe56e\", \"清算人员\", \"19999999999\"));\n        m.addAttribute(\"accountList\",list);\n        return \"account\";\n\t}\n}\n"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/resources/application.properties",
    "content": "server.context-path=/web\n\nspring.thymeleaf.cache=false"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/resources/static/css/style.css",
    "content": "table {\n\tmargin: 20px 40px 20px 0px;\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tborder-spacing: 0;\n\ttable-layout: automatic;\n\tword-wrap: break-all\n}\ntable>tbody>tr:nth-of-type(odd) {\n\tbackground-color: #F7F7F7\n}\n\nth, td {\n\tpadding: 8px;\n\ttext-align: left;\n\tvertical-align: middle;\n\tfont-weight: normal;\n\tfont-size: 12px;\n\tborder-bottom: 1px solid #fff;\n}\n\nth {\n\tpadding-bottom: 10px;\n    color: #fff;\n    font-weight: 700;\n    background: rgba(66, 185, 131, .9)\n}\n\ntd {\n\tborder-bottom-width: 1px\n}\n"
  },
  {
    "path": "08.Spring-Boot-Thymeleaf/src/main/resources/templates/account.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>account</title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n    <link rel=\"stylesheet\" th:href=\"@{/css/style.css}\" type=\"text/css\">\n</head>\n<body>\n    <table>\n\t\t<tr>\n\t\t\t<th>no</th>\n\t\t\t<th>account</th>\n\t\t\t<th>name</th>\n\t\t\t<th>password</th>\n\t\t\t<th>accountType</th>\n\t\t\t<th>tel</th>\n\t\t</tr>\n\t\t<tr th:each=\"list,stat : ${accountList}\">\n\t\t   <td th:text=\"${stat.count}\"></td>\n\t\t   <td th:text=\"${list.account}\"></td>\n\t\t   <td th:text=\"${list.name}\"></td>\n\t\t   <td th:text=\"${list.password}\"></td>\n\t\t   <td th:text=\"${list.accountType}\"></td>\n\t\t   <td th:text=\"${list.tel}\"></td>\n\t\t</tr>\n    </table>\n</body>\n</html>"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Redis-Cache</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-cache</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- spring-boot redis -->\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-data-redis</artifactId>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\n\n\n@SpringBootApplication\n@EnableCaching\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/ApplicationTest.java",
    "content": "package com.springboot;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\nimport com.springboot.bean.Student;\nimport com.springboot.service.StudentService;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ApplicationTest {\n\n\t@Autowired\n\tprivate StudentService studentService;\n\n\t@Test\n    public void test1() throws Exception {\n        Student student1 = this.studentService.queryStudentBySno(\"001\");\n        System.out.println(\"学号\" + student1.getSno() + \"的学生姓名为：\" + student1.getName());\n        \n        Student student2 = this.studentService.queryStudentBySno(\"001\");\n        System.out.println(\"学号\" + student2.getSno() + \"的学生姓名为：\" + student2.getName());\n    }\n\t\n\t@Test\n\tpublic void test2() throws Exception {\n\t\tStudent student1 = this.studentService.queryStudentBySno(\"001\");\n\t\tSystem.out.println(\"学号\" + student1.getSno() + \"的学生姓名为：\" + student1.getName());\n\n\t\tstudent1.setName(\"康康\");\n\t\tthis.studentService.update(student1);\n\t\t\n\t\tStudent student2 = this.studentService.queryStudentBySno(\"001\");\n\t\tSystem.out.println(\"学号\" + student2.getSno() + \"的学生姓名为：\" + student2.getName());\n\t}\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/bean/Student.java",
    "content": "package com.springboot.bean;\n\nimport java.io.Serializable;\n\npublic class Student implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -339516038496531943L;\n\tprivate String sno;\n\tprivate String name;\n\tprivate String sex;\n\tpublic String getSno() {\n\t\treturn sno;\n\t}\n\tpublic void setSno(String sno) {\n\t\tthis.sno = sno;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getSex() {\n\t\treturn sex;\n\t}\n\tpublic void setSex(String sex) {\n\t\tthis.sex = sex;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/config/RedisConfig.java",
    "content": "package com.springboot.config;\n\nimport org.springframework.cache.CacheManager;\nimport org.springframework.cache.annotation.CachingConfigurerSupport;\nimport org.springframework.cache.interceptor.KeyGenerator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.cache.RedisCacheManager;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@Configuration\npublic class RedisConfig extends CachingConfigurerSupport {\n\n\t// 自定义缓存key生成策略\n\t@Bean\n\tpublic KeyGenerator keyGenerator() {\n\t\treturn new KeyGenerator() {\n\t\t\t@Override\n\t\t\tpublic Object generate(Object target, java.lang.reflect.Method method, Object... params) {\n\t\t\t\tStringBuffer sb = new StringBuffer();\n\t\t\t\tsb.append(target.getClass().getName());\n\t\t\t\tsb.append(method.getName());\n\t\t\t\tfor (Object obj : params) {\n\t\t\t\t\tsb.append(obj.toString());\n\t\t\t\t}\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\t\t};\n\t}\n\n\t// 缓存管理器\n\t@Bean\n\tpublic CacheManager cacheManager(@SuppressWarnings(\"rawtypes\") RedisTemplate redisTemplate) {\n\t\tRedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);\n\t\t// 设置缓存过期时间\n\t\tcacheManager.setDefaultExpiration(10000);\n\t\treturn cacheManager;\n\t}\n\n\t@Bean\n\tpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {\n\t\tStringRedisTemplate template = new StringRedisTemplate(factory);\n\t\tsetSerializer(template);// 设置序列化工具\n\t\ttemplate.afterPropertiesSet();\n\t\treturn template;\n\t}\n\n\tprivate void setSerializer(StringRedisTemplate template) {\n\t\t@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n\t\tJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n\t\tObjectMapper om = new ObjectMapper();\n\t\tom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n\t\tom.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n\t\tjackson2JsonRedisSerializer.setObjectMapper(om);\n\t\ttemplate.setValueSerializer(jackson2JsonRedisSerializer);\n\t}\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/mapper/StudentMapper.java",
    "content": "package com.springboot.mapper;\n\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Result;\nimport org.apache.ibatis.annotations.Results;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.cache.annotation.CacheConfig;\n\nimport com.springboot.bean.Student;\n\n@Mapper\n@CacheConfig(cacheNames = \"student\")\npublic interface StudentMapper {\n\n\t@Update(\"update student set sname=#{name},ssex=#{sex} where sno=#{sno}\")\n\tint update(Student student);\n\n\t@Delete(\"delete from student where sno=#{sno}\")\n\tvoid deleteStudentBySno(String sno);\n\n\t@Select(\"select * from student where sno=#{sno}\")\n\t@Results(id = \"student\", value = { @Result(property = \"sno\", column = \"sno\", javaType = String.class),\n\t\t\t@Result(property = \"name\", column = \"sname\", javaType = String.class),\n\t\t\t@Result(property = \"sex\", column = \"ssex\", javaType = String.class) })\n\tStudent queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport org.springframework.cache.annotation.CacheConfig;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\n\nimport com.springboot.bean.Student;\n\n@CacheConfig(cacheNames = \"student\")\npublic interface StudentService {\n\t@CachePut(key = \"#p0.sno\")\n\tStudent update(Student student);\n\n\t@CacheEvict(key = \"#p0\", allEntries = true)\n\tvoid deleteStudentBySno(String sno);\n\t\n\t@Cacheable(key = \"#p0\")\n\tStudent queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/java/com/springboot/service/impl/StudentServiceImpl.java",
    "content": "package com.springboot.service.impl;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.bean.Student;\nimport com.springboot.mapper.StudentMapper;\nimport com.springboot.service.StudentService;\n\n@Repository(\"studentService\")\npublic class StudentServiceImpl implements StudentService{\n\n\t@Autowired\n\tprivate StudentMapper studentMapper;\n\t\n\t@Override\n\tpublic Student update(Student student) {\n\t\tthis.studentMapper.update(student);\n\t\treturn this.studentMapper.queryStudentBySno(student.getSno());\n\t}\n\n\t@Override\n\tpublic void deleteStudentBySno(String sno) {\n\t\tthis.studentMapper.deleteStudentBySno(sno);\n\t}\n\n\t@Override\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentMapper.queryStudentBySno(sno);\n\t}\n\n}\n"
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n          \n  redis:\n    # Redis数据库索引（默认为0）\n    database: 0\n    # Redis服务器地址\n    host: localhost\n    # Redis服务器连接端口\n    port: 6379\n    pool:\n      # 连接池最大连接数（使用负值表示没有限制）\n      max-active: 8\n      # 连接池最大阻塞等待时间（使用负值表示没有限制）\n      max-wait: -1\n      # 连接池中的最大空闲连接\n      max-idle: 8\n      # 连接池中的最小空闲连接\n      min-idle: 0\n    # 连接超时时间（毫秒）\n    timeout: 0\n    \n          \nlogging:\n  level:\n    com:\n      springboot:\n        mapper: debug\n     "
  },
  {
    "path": "09.Spring-Boot-Redis-Cache/src/main/resources/init.sql",
    "content": "CREATE TABLE STUDENT (\n    SNO VARCHAR2(3 BYTE) NOT NULL ,\n    SNAME VARCHAR2(9 BYTE) NOT NULL ,\n    SSEX CHAR(2 BYTE) NOT NULL \n);\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ');"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Ehcache-Cache</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-cache</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- ehcache -->\n\t\t<dependency>\n\t\t    <groupId>net.sf.ehcache</groupId>\n\t\t    <artifactId>ehcache</artifactId>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\n\n\n@SpringBootApplication\n@EnableCaching\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/ApplicationTest.java",
    "content": "package com.springboot;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\nimport com.springboot.bean.Student;\nimport com.springboot.service.StudentService;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ApplicationTest {\n\n\t@Autowired\n\tprivate StudentService studentService;\n\t\n\n\t@Test\n    public void test1() throws Exception {\n        Student student1 = this.studentService.queryStudentBySno(\"001\");\n        System.out.println(\"学号\" + student1.getSno() + \"的学生姓名为：\" + student1.getName());\n        \n        Student student2 = this.studentService.queryStudentBySno(\"001\");\n        System.out.println(\"学号\" + student2.getSno() + \"的学生姓名为：\" + student2.getName());\n    }\n\t\n\t@Test\n\tpublic void test2() throws Exception {\n\t\t\n\t\tStudent student1 = this.studentService.queryStudentBySno(\"001\");\n\t\tSystem.out.println(\"学号\" + student1.getSno() + \"的学生姓名为：\" + student1.getName());\n\n\t\tstudent1.setName(\"康康\");\n\t\tthis.studentService.update(student1);\n\t\t\n\t\tStudent student2 = this.studentService.queryStudentBySno(\"001\");\n\t\tSystem.out.println(\"学号\" + student2.getSno() + \"的学生姓名为：\" + student2.getName());\n\t}\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/bean/Student.java",
    "content": "package com.springboot.bean;\n\nimport java.io.Serializable;\n\npublic class Student implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -339516038496531943L;\n\tprivate String sno;\n\tprivate String name;\n\tprivate String sex;\n\tpublic String getSno() {\n\t\treturn sno;\n\t}\n\tpublic void setSno(String sno) {\n\t\tthis.sno = sno;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getSex() {\n\t\treturn sex;\n\t}\n\tpublic void setSex(String sex) {\n\t\tthis.sex = sex;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/mapper/StudentMapper.java",
    "content": "package com.springboot.mapper;\n\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Result;\nimport org.apache.ibatis.annotations.Results;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\n\nimport com.springboot.bean.Student;\n\n@Mapper\npublic interface StudentMapper {\n\n\t@Update(\"update student set sname=#{name},ssex=#{sex} where sno=#{sno}\")\n\tint update(Student student);\n\n\t@Delete(\"delete from student where sno=#{sno}\")\n\tvoid deleteStudentBySno(String sno);\n\n\t@Select(\"select * from student where sno=#{sno}\")\n\t@Results(id = \"student\", value = { @Result(property = \"sno\", column = \"sno\", javaType = String.class),\n\t\t\t@Result(property = \"name\", column = \"sname\", javaType = String.class),\n\t\t\t@Result(property = \"sex\", column = \"ssex\", javaType = String.class) })\n\tStudent queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/service/StudentService.java",
    "content": "package com.springboot.service;\n\nimport org.springframework.cache.annotation.CacheConfig;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\n\nimport com.springboot.bean.Student;\n\n@CacheConfig(cacheNames = \"student\")\npublic interface StudentService {\n\t@CachePut(key = \"#p0.sno\")\n\tStudent update(Student student);\n\n\t@CacheEvict(key = \"#p0\", allEntries = true)\n\tvoid deleteStudentBySno(String sno);\n\t\n\t@Cacheable(key = \"#p0\")\n\tStudent queryStudentBySno(String sno);\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/java/com/springboot/service/impl/StudentServiceImpl.java",
    "content": "package com.springboot.service.impl;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.bean.Student;\nimport com.springboot.mapper.StudentMapper;\nimport com.springboot.service.StudentService;\n\n@Repository(\"studentService\")\npublic class StudentServiceImpl implements StudentService{\n\n\t@Autowired\n\tprivate StudentMapper studentMapper;\n\t\n\t@Override\n\tpublic Student update(Student student) {\n\t\tthis.studentMapper.update(student);\n\t\treturn this.studentMapper.queryStudentBySno(student.getSno());\n\t}\n\n\t@Override\n\tpublic void deleteStudentBySno(String sno) {\n\t\tthis.studentMapper.deleteStudentBySno(sno);\n\t}\n\n\t@Override\n\tpublic Student queryStudentBySno(String sno) {\n\t\treturn this.studentMapper.queryStudentBySno(sno);\n\t}\n\n}\n"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n\n  cache:\n    ehcache:\n      config: 'classpath:ehcache.xml'    \n          \nlogging:\n  level:\n    com:\n      springboot:\n        mapper: debug\n     "
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/resources/ehcache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"ehcache.xsd\">\n\t <defaultCache\n        maxElementsInMemory=\"10000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"3600\"\n        timeToLiveSeconds=\"0\"\n        overflowToDisk=\"false\"\n        diskPersistent=\"false\"\n        diskExpiryThreadIntervalSeconds=\"120\" />\n\n    <cache \n    \tname=\"student\"\n        maxEntriesLocalHeap=\"2000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"3600\"\n        timeToLiveSeconds=\"0\"\n        overflowToDisk=\"false\"\n        statistics=\"true\">\n    </cache>\n</ehcache>"
  },
  {
    "path": "10.Spring-Boot-Ehcache-Cache/src/main/resources/init.sql",
    "content": "CREATE TABLE STUDENT (\n    SNO VARCHAR2(3 BYTE) NOT NULL ,\n    SNAME VARCHAR2(9 BYTE) NOT NULL ,\n    SSEX CHAR(2 BYTE) NOT NULL \n);\nINSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ');\nINSERT INTO STUDENT VALUES ('002', 'Mike', 'M ');\nINSERT INTO STUDENT VALUES ('003', 'Jane', 'F ');"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-Authentication</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.springboot.shiro.ShiroRealm;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\t\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"authc\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       return securityManager;  \n    }  \n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"test\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n     "
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'test', '7a38c13ec5e9310aed731de58bbc4214', TO_DATE('2017-11-19 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-11-19 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Primary Key structure for table T_USER\n-- ----------------------------\nALTER TABLE T_USER ADD PRIMARY KEY (ID);"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "11.Spring-Boot-Shiro-Authentication/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-RememberMe</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.LifecycleBeanPostProcessor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.springboot.shiro.ShiroRealm;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\t\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       return securityManager;  \n    }  \n\t\n\t@Bean(name = \"lifecycleBeanPostProcessor\")\n    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {\n        return new LifecycleBeanPostProcessor();\n    }\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\t/**\n\t * cookie对象\n\t * @return\n\t */\n\tpublic SimpleCookie rememberMeCookie() {\n\t\t// 设置cookie名称，对应login.html页面的<input type=\"checkbox\" name=\"rememberMe\"/>\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\t// 设置cookie的过期时间，单位为秒，这里为一天\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\t/**\n\t * cookie管理对象\n\t * @return\n\t */\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\t// rememberMe cookie加密的密钥 \n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"3AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\t\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"test\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n     "
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'test', '7a38c13ec5e9310aed731de58bbc4214', TO_DATE('2017-11-19 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '0');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-11-19 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Primary Key structure for table T_USER\n-- ----------------------------\nALTER TABLE T_USER ADD PRIMARY KEY (ID);"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "12.Spring-Boot-Shiro-RememberMe/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-Authorization</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\n\nimport com.springboot.shiro.ShiroRealm;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       return securityManager;  \n    }  \n\t\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\tpublic SimpleCookie rememberMeCookie() {\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"4AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\t\n\t@GetMapping(\"/403\")\n\tpublic String forbid() {\n\t\treturn \"403\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/controller/UserController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\t\n\t\n\t@RequiresPermissions(\"user:user\")\n\t@RequestMapping(\"list\")\n\tpublic String userList(Model model) {\n\t\tmodel.addAttribute(\"value\", \"获取用户信息\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:add\")\n\t@RequestMapping(\"add\")\n\tpublic String userAdd(Model model) {\n\t\tmodel.addAttribute(\"value\", \"新增用户\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:delete\")\n\t@RequestMapping(\"delete\")\n\tpublic String userDelete(Model model) {\n\t\tmodel.addAttribute(\"value\", \"删除用户\");\n\t\treturn \"user\";\n\t}\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/dao/UserPermissionMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Permission;\n\n@Mapper\npublic interface UserPermissionMapper {\n\t\n\tList<Permission> findByUserName(String userName);\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/dao/UserRoleMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Role;\n\n@Mapper\npublic interface UserRoleMapper {\n\t\n\tList<Role> findByUserName(String userName);\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/handler/GlobalExceptionHandler.java",
    "content": "package com.springboot.handler;\n\nimport org.apache.shiro.authz.AuthorizationException;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\n\n\n@ControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\t\n\t@ExceptionHandler(value = AuthorizationException.class)\n\tpublic String handleAuthorizationException() {\n\t\treturn \"403\";\n\t}\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/pojo/Permission.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Permission implements Serializable{\n\n\tprivate static final long serialVersionUID = 7160557680614732403L;\n\tprivate Integer id;\n\tprivate String url;\n\tprivate String name;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/pojo/Role.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Role implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -227437593919820521L;\n\tprivate Integer id;\n\tprivate String name;\n\tprivate String memo;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getMemo() {\n\t\treturn memo;\n\t}\n\tpublic void setMemo(String memo) {\n\t\tthis.memo = memo;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.dao.UserPermissionMapper;\nimport com.springboot.dao.UserRoleMapper;\nimport com.springboot.pojo.Permission;\nimport com.springboot.pojo.Role;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\t@Autowired\n\tprivate UserRoleMapper userRoleMapper;\n\t@Autowired\n\tprivate UserPermissionMapper userPermissionMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tString userName = user.getUserName();\n\n\t\tSystem.out.println(\"用户\" + userName + \"获取权限-----ShiroRealm.doGetAuthorizationInfo\");\n\t\tSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n\t\t// 获取用户角色集\n\t\tList<Role> roleList = userRoleMapper.findByUserName(userName);\n\t\tSet<String> roleSet = new HashSet<String>();\n\t\tfor (Role r : roleList) {\n\t\t\troleSet.add(r.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setRoles(roleSet);\n\n\t\t// 获取用户权限集\n\t\tList<Permission> permissionList = userPermissionMapper.findByUserName(userName);\n\t\tSet<String> permissionSet = new HashSet<String>();\n\t\tfor (Permission p : permissionList) {\n\t\t\tpermissionSet.add(p.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setStringPermissions(permissionSet);\n\t\treturn simpleAuthorizationInfo;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"tester\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n     "
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_PERMISSION\n-- ----------------------------\nCREATE TABLE T_PERMISSION (\n   ID NUMBER(10) NOT NULL ,\n   URL VARCHAR2(256 BYTE) NULL ,\n   NAME VARCHAR2(64 BYTE) NULL \n);\nCOMMENT ON COLUMN T_PERMISSION.URL IS 'url地址';\nCOMMENT ON COLUMN T_PERMISSION.NAME IS 'url描述';\n-- ----------------------------\n-- Records of T_PERMISSION\n-- ----------------------------\nINSERT INTO T_PERMISSION VALUES ('1', '/user', 'user:user');\nINSERT INTO T_PERMISSION VALUES ('2', '/user/add', 'user:add');\nINSERT INTO T_PERMISSION VALUES ('3', '/user/delete', 'user:delete');\n-- ----------------------------\n-- Table structure for T_ROLE\n-- ----------------------------\nCREATE TABLE T_ROLE (\n   ID NUMBER NOT NULL ,\n   NAME VARCHAR2(32 BYTE) NULL ,\n   MEMO VARCHAR2(32 BYTE) NULL \n);\nCOMMENT ON COLUMN T_ROLE.NAME IS '角色名称';\nCOMMENT ON COLUMN T_ROLE.MEMO IS '角色描述';\n-- ----------------------------\n-- Records of T_ROLE\n-- ----------------------------\nINSERT INTO T_ROLE VALUES ('1', 'admin', '超级管理员');\nINSERT INTO T_ROLE VALUES ('2', 'test', '测试账户');\n-- ----------------------------\n-- Table structure for T_ROLE_PERMISSION\n-- ----------------------------\nCREATE TABLE T_ROLE_PERMISSION (\n   RID NUMBER(10) NULL ,\n   PID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_ROLE_PERMISSION.RID IS '角色id';\nCOMMENT ON COLUMN T_ROLE_PERMISSION.PID IS '权限id';\n-- ----------------------------\n-- Records of T_ROLE_PERMISSION\n-- ----------------------------\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '2');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '3');\nINSERT INTO T_ROLE_PERMISSION VALUES ('2', '1');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '1');\n-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Table structure for T_USER_ROLE\n-- ----------------------------\nCREATE TABLE T_USER_ROLE (\n   USER_ID NUMBER(10) NULL ,\n   RID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_USER_ROLE.USER_ID IS '用户id';\nCOMMENT ON COLUMN T_USER_ROLE.RID IS '角色id';\n-- ----------------------------\n-- Records of T_USER_ROLE\n-- ----------------------------\nINSERT INTO T_USER_ROLE VALUES ('1', '1');\nINSERT INTO T_USER_ROLE VALUES ('2', '2');"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/mapper/UserPermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserPermissionMapper\">\n\n<resultMap type=\"com.springboot.pojo.Permission\" id=\"permission\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"url\" property=\"url\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"permission\">\n\tselect p.id,p.url,p.name from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\tleft join t_role_permission rp on(rp.rid = r.id) \n\tleft join t_permission p on(p.id = rp.pid ) \n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/mapper/UserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserRoleMapper\">\n\n<resultMap type=\"com.springboot.pojo.Role\" id=\"role\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"memo\" property=\"memo\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"role\">\n\tselect r.id,r.name,r.memo from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/templates/403.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>暂无权限</title>\n</head>\n<body>\n\t<p>您没有权限访问该资源！！</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<style>\n\tdiv {\n\t\tborder: 1px dashed #ddd;\n\t\tpadding: 10px;\n\t\tmargin: 10px 10px 10px 0px;\n\t}\n</style>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<h3>权限测试链接</h3>\n\t<div>\n\t\t<a th:href=\"@{/user/list}\">获取用户信息</a>\n\t\t<a th:href=\"@{/user/add}\">新增用户</a>\n\t\t<a th:href=\"@{/user/delete}\">删除用户</a>\n\t</div>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "13.Spring-Boot-Shiro-Authorization/src/main/resources/templates/user.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>[[${value}]]</title>\n</head>\n<body>\n\t<p>[[${value}]]</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-Redis</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-redis -->\n\t\t<dependency>\n\t\t    <groupId>org.crazycake</groupId>\n\t\t    <artifactId>shiro-redis</artifactId>\n\t\t    <version>2.4.2.1-RELEASE</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.LifecycleBeanPostProcessor;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.crazycake.shiro.RedisCacheManager;\nimport org.crazycake.shiro.RedisManager;\nimport org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\n\nimport com.springboot.shiro.ShiroRealm;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       securityManager.setCacheManager(cacheManager());\n       return securityManager;  \n    }  \n\t\n\t@Bean(name = \"lifecycleBeanPostProcessor\")\n    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {\n        return new LifecycleBeanPostProcessor();\n    }\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\tpublic SimpleCookie rememberMeCookie() {\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"4AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\t\n\t@Bean\n    @DependsOn({\"lifecycleBeanPostProcessor\"})\n    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {\n        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();\n        advisorAutoProxyCreator.setProxyTargetClass(true);\n        return advisorAutoProxyCreator;\n    }\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n    \n\tpublic RedisManager redisManager() {\n\t\tRedisManager redisManager = new RedisManager();\n\t\treturn redisManager;\n\t}\n\n\tpublic RedisCacheManager cacheManager() {\n\t\tRedisCacheManager redisCacheManager = new RedisCacheManager();\n\t\tredisCacheManager.setRedisManager(redisManager());\n\t\treturn redisCacheManager;\n\t}\n\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\t\n\t@GetMapping(\"/403\")\n\tpublic String forbid() {\n\t\treturn \"403\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/controller/UserController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\t\n\t\n\t@RequiresPermissions(\"user:user\")\n\t@RequestMapping(\"list\")\n\tpublic String userList(Model model) {\n\t\tmodel.addAttribute(\"value\", \"获取用户信息\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:add\")\n\t@RequestMapping(\"add\")\n\tpublic String userAdd(Model model) {\n\t\tmodel.addAttribute(\"value\", \"新增用户\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:delete\")\n\t@RequestMapping(\"delete\")\n\tpublic String userDelete(Model model) {\n\t\tmodel.addAttribute(\"value\", \"删除用户\");\n\t\treturn \"user\";\n\t}\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/dao/UserPermissionMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Permission;\n\n@Mapper\npublic interface UserPermissionMapper {\n\t\n\tList<Permission> findByUserName(String userName);\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/dao/UserRoleMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Role;\n\n@Mapper\npublic interface UserRoleMapper {\n\t\n\tList<Role> findByUserName(String userName);\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/handler/GlobalExceptionHandler.java",
    "content": "package com.springboot.handler;\n\nimport org.apache.shiro.authz.AuthorizationException;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\n\n\n@ControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\t\n\t@ExceptionHandler(value = AuthorizationException.class)\n\tpublic String handleAuthorizationException() {\n\t\treturn \"403\";\n\t}\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/pojo/Permission.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Permission implements Serializable{\n\n\tprivate static final long serialVersionUID = 7160557680614732403L;\n\tprivate Integer id;\n\tprivate String url;\n\tprivate String name;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/pojo/Role.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Role implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -227437593919820521L;\n\tprivate Integer id;\n\tprivate String name;\n\tprivate String memo;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getMemo() {\n\t\treturn memo;\n\t}\n\tpublic void setMemo(String memo) {\n\t\tthis.memo = memo;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.dao.UserPermissionMapper;\nimport com.springboot.dao.UserRoleMapper;\nimport com.springboot.pojo.Permission;\nimport com.springboot.pojo.Role;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\t@Autowired\n\tprivate UserRoleMapper userRoleMapper;\n\t@Autowired\n\tprivate UserPermissionMapper userPermissionMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tString userName = user.getUserName();\n\n\t\tSystem.out.println(\"用户\" + userName + \"获取权限-----ShiroRealm.doGetAuthorizationInfo\");\n\t\tSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n\t\t// 获取用户角色集\n\t\tList<Role> roleList = userRoleMapper.findByUserName(userName);\n\t\tSet<String> roleSet = new HashSet<String>();\n\t\tfor (Role r : roleList) {\n\t\t\troleSet.add(r.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setRoles(roleSet);\n\n\t\t// 获取用户权限集\n\t\tList<Permission> permissionList = userPermissionMapper.findByUserName(userName);\n\t\tSet<String> permissionSet = new HashSet<String>();\n\t\tfor (Permission p : permissionList) {\n\t\t\tpermissionSet.add(p.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setStringPermissions(permissionSet);\n\t\treturn simpleAuthorizationInfo;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"tester\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n\n  redis:\n    host: localhost\n    port: 6379\n    pool:\n      max-active: 8\n      max-wait: -1\n      max-idle: 8\n      min-idle: 0\n    timeout: 0\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n     "
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_PERMISSION\n-- ----------------------------\nCREATE TABLE T_PERMISSION (\n   ID NUMBER(10) NOT NULL ,\n   URL VARCHAR2(256 BYTE) NULL ,\n   NAME VARCHAR2(64 BYTE) NULL \n);\nCOMMENT ON COLUMN T_PERMISSION.URL IS 'url地址';\nCOMMENT ON COLUMN T_PERMISSION.NAME IS 'url描述';\n-- ----------------------------\n-- Records of T_PERMISSION\n-- ----------------------------\nINSERT INTO T_PERMISSION VALUES ('1', '/user', 'user:user');\nINSERT INTO T_PERMISSION VALUES ('2', '/user/add', 'user:add');\nINSERT INTO T_PERMISSION VALUES ('3', '/user/delete', 'user:delete');\n-- ----------------------------\n-- Table structure for T_ROLE\n-- ----------------------------\nCREATE TABLE T_ROLE (\n   ID NUMBER NOT NULL ,\n   NAME VARCHAR2(32 BYTE) NULL ,\n   MEMO VARCHAR2(32 BYTE) NULL \n);\nCOMMENT ON COLUMN T_ROLE.NAME IS '角色名称';\nCOMMENT ON COLUMN T_ROLE.MEMO IS '角色描述';\n-- ----------------------------\n-- Records of T_ROLE\n-- ----------------------------\nINSERT INTO T_ROLE VALUES ('1', 'admin', '超级管理员');\nINSERT INTO T_ROLE VALUES ('2', 'test', '测试账户');\n-- ----------------------------\n-- Table structure for T_ROLE_PERMISSION\n-- ----------------------------\nCREATE TABLE T_ROLE_PERMISSION (\n   RID NUMBER(10) NULL ,\n   PID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_ROLE_PERMISSION.RID IS '角色id';\nCOMMENT ON COLUMN T_ROLE_PERMISSION.PID IS '权限id';\n-- ----------------------------\n-- Records of T_ROLE_PERMISSION\n-- ----------------------------\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '2');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '3');\nINSERT INTO T_ROLE_PERMISSION VALUES ('2', '1');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '1');\n-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Table structure for T_USER_ROLE\n-- ----------------------------\nCREATE TABLE T_USER_ROLE (\n   USER_ID NUMBER(10) NULL ,\n   RID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_USER_ROLE.USER_ID IS '用户id';\nCOMMENT ON COLUMN T_USER_ROLE.RID IS '角色id';\n-- ----------------------------\n-- Records of T_USER_ROLE\n-- ----------------------------\nINSERT INTO T_USER_ROLE VALUES ('1', '1');\nINSERT INTO T_USER_ROLE VALUES ('2', '2');"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/mapper/UserPermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserPermissionMapper\">\n\n<resultMap type=\"com.springboot.pojo.Permission\" id=\"permission\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"url\" property=\"url\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"permission\">\n\tselect p.id,p.url,p.name from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\tleft join t_role_permission rp on(rp.rid = r.id) \n\tleft join t_permission p on(p.id = rp.pid ) \n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/mapper/UserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserRoleMapper\">\n\n<resultMap type=\"com.springboot.pojo.Role\" id=\"role\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"memo\" property=\"memo\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"role\">\n\tselect r.id,r.name,r.memo from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/templates/403.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>暂无权限</title>\n</head>\n<body>\n\t<p>您没有权限访问该资源！！</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<style>\n\tdiv {\n\t\tborder: 1px dashed #ddd;\n\t\tpadding: 10px;\n\t\tmargin: 10px 10px 10px 0px;\n\t}\n</style>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<h3>权限测试链接</h3>\n\t<div>\n\t\t<a th:href=\"@{/user/list}\">获取用户信息</a>\n\t\t<a th:href=\"@{/user/add}\">新增用户</a>\n\t\t<a th:href=\"@{/user/delete}\">删除用户</a>\n\t</div>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "14.Spring-Boot-Shiro-Redis/src/main/resources/templates/user.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>[[${value}]]</title>\n</head>\n<body>\n\t<p>[[${value}]]</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-Ehcache</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro ehcache -->\n        <dependency>\n            <groupId>org.apache.shiro</groupId>\n            <artifactId>shiro-ehcache</artifactId>\n            <version>1.3.2</version>\n        </dependency>\n        <!-- ehchache -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-cache</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>net.sf.ehcache</groupId>\n            <artifactId>ehcache</artifactId>\n        </dependency>\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.cache.ehcache.EhCacheManager;\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.LifecycleBeanPostProcessor;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\n\nimport com.springboot.shiro.ShiroRealm;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic EhCacheManager getEhCacheManager() {\n\t\tEhCacheManager em = new EhCacheManager();\n\t\tem.setCacheManagerConfigFile(\"classpath:config/shiro-ehcache.xml\");\n\t\treturn em;\n\t}\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       securityManager.setCacheManager(getEhCacheManager());\n       return securityManager;  \n    }  \n\t\n\t@Bean(name = \"lifecycleBeanPostProcessor\")\n    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {\n        return new LifecycleBeanPostProcessor();\n    }\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\tpublic SimpleCookie rememberMeCookie() {\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"4AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\t\n\t@Bean\n    @DependsOn({\"lifecycleBeanPostProcessor\"})\n    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {\n        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();\n        advisorAutoProxyCreator.setProxyTargetClass(true);\n        return advisorAutoProxyCreator;\n    }\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\t\n\t@GetMapping(\"/403\")\n\tpublic String forbid() {\n\t\treturn \"403\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/controller/UserController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\t\n\t\n\t@RequiresPermissions(\"user:user\")\n\t@RequestMapping(\"list\")\n\tpublic String userList(Model model) {\n\t\tmodel.addAttribute(\"value\", \"获取用户信息\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:add\")\n\t@RequestMapping(\"add\")\n\tpublic String userAdd(Model model) {\n\t\tmodel.addAttribute(\"value\", \"新增用户\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:delete\")\n\t@RequestMapping(\"delete\")\n\tpublic String userDelete(Model model) {\n\t\tmodel.addAttribute(\"value\", \"删除用户\");\n\t\treturn \"user\";\n\t}\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/dao/UserPermissionMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Permission;\n\n@Mapper\npublic interface UserPermissionMapper {\n\t\n\tList<Permission> findByUserName(String userName);\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/dao/UserRoleMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Role;\n\n@Mapper\npublic interface UserRoleMapper {\n\t\n\tList<Role> findByUserName(String userName);\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/handler/GlobalExceptionHandler.java",
    "content": "package com.springboot.handler;\n\nimport org.apache.shiro.authz.AuthorizationException;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\n\n\n@ControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\t\n\t@ExceptionHandler(value = AuthorizationException.class)\n\tpublic String handleAuthorizationException() {\n\t\treturn \"403\";\n\t}\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/pojo/Permission.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Permission implements Serializable{\n\n\tprivate static final long serialVersionUID = 7160557680614732403L;\n\tprivate Integer id;\n\tprivate String url;\n\tprivate String name;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/pojo/Role.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Role implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -227437593919820521L;\n\tprivate Integer id;\n\tprivate String name;\n\tprivate String memo;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getMemo() {\n\t\treturn memo;\n\t}\n\tpublic void setMemo(String memo) {\n\t\tthis.memo = memo;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.dao.UserPermissionMapper;\nimport com.springboot.dao.UserRoleMapper;\nimport com.springboot.pojo.Permission;\nimport com.springboot.pojo.Role;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\t@Autowired\n\tprivate UserRoleMapper userRoleMapper;\n\t@Autowired\n\tprivate UserPermissionMapper userPermissionMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tString userName = user.getUserName();\n\n\t\tSystem.out.println(\"用户\" + userName + \"获取权限-----ShiroRealm.doGetAuthorizationInfo\");\n\t\tSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n\t\t// 获取用户角色集\n\t\tList<Role> roleList = userRoleMapper.findByUserName(userName);\n\t\tSet<String> roleSet = new HashSet<String>();\n\t\tfor (Role r : roleList) {\n\t\t\troleSet.add(r.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setRoles(roleSet);\n\n\t\t// 获取用户权限集\n\t\tList<Permission> permissionList = userPermissionMapper.findByUserName(userName);\n\t\tSet<String> permissionSet = new HashSet<String>();\n\t\tfor (Permission p : permissionList) {\n\t\t\tpermissionSet.add(p.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setStringPermissions(permissionSet);\n\t\treturn simpleAuthorizationInfo;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"tester\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n     "
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/config/shiro-ehcache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:noNamespaceSchemaLocation=\"http://ehcache.org/ehcache.xsd\"\n\tupdateCheck=\"false\">\n\t<diskStore path=\"java.io.tmpdir/Tmp_EhCache\" />\n\t<defaultCache\n        maxElementsInMemory=\"10000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"120\"\n        timeToLiveSeconds=\"120\"\n        overflowToDisk=\"false\"\n        diskPersistent=\"false\"\n        diskExpiryThreadIntervalSeconds=\"120\" />\n        \n\t<!-- 登录记录缓存锁定1小时 -->\n    <cache name=\"passwordRetryCache\"\n        maxEntriesLocalHeap=\"2000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"3600\"\n        timeToLiveSeconds=\"0\"\n        overflowToDisk=\"false\"\n        statistics=\"true\" />\n</ehcache>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_PERMISSION\n-- ----------------------------\nCREATE TABLE T_PERMISSION (\n   ID NUMBER(10) NOT NULL ,\n   URL VARCHAR2(256 BYTE) NULL ,\n   NAME VARCHAR2(64 BYTE) NULL \n);\nCOMMENT ON COLUMN T_PERMISSION.URL IS 'url地址';\nCOMMENT ON COLUMN T_PERMISSION.NAME IS 'url描述';\n-- ----------------------------\n-- Records of T_PERMISSION\n-- ----------------------------\nINSERT INTO T_PERMISSION VALUES ('1', '/user', 'user:user');\nINSERT INTO T_PERMISSION VALUES ('2', '/user/add', 'user:add');\nINSERT INTO T_PERMISSION VALUES ('3', '/user/delete', 'user:delete');\n-- ----------------------------\n-- Table structure for T_ROLE\n-- ----------------------------\nCREATE TABLE T_ROLE (\n   ID NUMBER NOT NULL ,\n   NAME VARCHAR2(32 BYTE) NULL ,\n   MEMO VARCHAR2(32 BYTE) NULL \n);\nCOMMENT ON COLUMN T_ROLE.NAME IS '角色名称';\nCOMMENT ON COLUMN T_ROLE.MEMO IS '角色描述';\n-- ----------------------------\n-- Records of T_ROLE\n-- ----------------------------\nINSERT INTO T_ROLE VALUES ('1', 'admin', '超级管理员');\nINSERT INTO T_ROLE VALUES ('2', 'test', '测试账户');\n-- ----------------------------\n-- Table structure for T_ROLE_PERMISSION\n-- ----------------------------\nCREATE TABLE T_ROLE_PERMISSION (\n   RID NUMBER(10) NULL ,\n   PID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_ROLE_PERMISSION.RID IS '角色id';\nCOMMENT ON COLUMN T_ROLE_PERMISSION.PID IS '权限id';\n-- ----------------------------\n-- Records of T_ROLE_PERMISSION\n-- ----------------------------\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '2');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '3');\nINSERT INTO T_ROLE_PERMISSION VALUES ('2', '1');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '1');\n-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Table structure for T_USER_ROLE\n-- ----------------------------\nCREATE TABLE T_USER_ROLE (\n   USER_ID NUMBER(10) NULL ,\n   RID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_USER_ROLE.USER_ID IS '用户id';\nCOMMENT ON COLUMN T_USER_ROLE.RID IS '角色id';\n-- ----------------------------\n-- Records of T_USER_ROLE\n-- ----------------------------\nINSERT INTO T_USER_ROLE VALUES ('1', '1');\nINSERT INTO T_USER_ROLE VALUES ('2', '2');"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/mapper/UserPermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserPermissionMapper\">\n\n<resultMap type=\"com.springboot.pojo.Permission\" id=\"permission\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"url\" property=\"url\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"permission\">\n\tselect p.id,p.url,p.name from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\tleft join t_role_permission rp on(rp.rid = r.id) \n\tleft join t_permission p on(p.id = rp.pid ) \n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/mapper/UserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserRoleMapper\">\n\n<resultMap type=\"com.springboot.pojo.Role\" id=\"role\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"memo\" property=\"memo\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"role\">\n\tselect r.id,r.name,r.memo from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/templates/403.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>暂无权限</title>\n</head>\n<body>\n\t<p>您没有权限访问该资源！！</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<style>\n\tdiv {\n\t\tborder: 1px dashed #ddd;\n\t\tpadding: 10px;\n\t\tmargin: 10px 10px 10px 0px;\n\t}\n</style>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<h3>权限测试链接</h3>\n\t<div>\n\t\t<a th:href=\"@{/user/list}\">获取用户信息</a>\n\t\t<a th:href=\"@{/user/add}\">新增用户</a>\n\t\t<a th:href=\"@{/user/delete}\">删除用户</a>\n\t</div>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "15.Spring-Boot-Shiro-Ehcache/src/main/resources/templates/user.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>[[${value}]]</title>\n</head>\n<body>\n\t<p>[[${value}]]</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Shiro-Thymeleaf-Tag</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro ehcache -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-ehcache</artifactId>\n\t\t    <version>1.3.2</version>\n\t\t</dependency>\n\t\t<!-- ehchache -->\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-cache</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t    <groupId>net.sf.ehcache</groupId>\n\t\t    <artifactId>ehcache</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->\n\t\t<dependency>\n\t\t    <groupId>com.github.theborakompanioni</groupId>\n\t\t    <artifactId>thymeleaf-extras-shiro</artifactId>\n\t\t    <version>2.0.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.cache.ehcache.EhCacheManager;\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.LifecycleBeanPostProcessor;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\n\nimport com.springboot.shiro.ShiroRealm;\n\nimport at.pollux.thymeleaf.shiro.dialect.ShiroDialect;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic EhCacheManager getEhCacheManager() {\n\t    EhCacheManager em = new EhCacheManager();\n\t    em.setCacheManagerConfigFile(\"classpath:config/shiro-ehcache.xml\");\n\t    return em;\n\t}\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       securityManager.setCacheManager(getEhCacheManager());\n       return securityManager;  \n    }  \n\t\n\t@Bean(name = \"lifecycleBeanPostProcessor\")\n    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {\n        return new LifecycleBeanPostProcessor();\n    }\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\tpublic SimpleCookie rememberMeCookie() {\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"4AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\t\n\t@Bean\n    @DependsOn({\"lifecycleBeanPostProcessor\"})\n    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {\n        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();\n        advisorAutoProxyCreator.setProxyTargetClass(true);\n        return advisorAutoProxyCreator;\n    }\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n    \n    @Bean\n\tpublic ShiroDialect shiroDialect() {\n\t\treturn new ShiroDialect();\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\t\n\t@GetMapping(\"/403\")\n\tpublic String forbid() {\n\t\treturn \"403\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/controller/UserController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\t\n\t\n\t@RequiresPermissions(\"user:user\")\n\t@RequestMapping(\"list\")\n\tpublic String userList(Model model) {\n\t\tmodel.addAttribute(\"value\", \"获取用户信息\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:add\")\n\t@RequestMapping(\"add\")\n\tpublic String userAdd(Model model) {\n\t\tmodel.addAttribute(\"value\", \"新增用户\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:delete\")\n\t@RequestMapping(\"delete\")\n\tpublic String userDelete(Model model) {\n\t\tmodel.addAttribute(\"value\", \"删除用户\");\n\t\treturn \"user\";\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/dao/UserPermissionMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Permission;\n\n@Mapper\npublic interface UserPermissionMapper {\n\t\n\tList<Permission> findByUserName(String userName);\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/dao/UserRoleMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Role;\n\n@Mapper\npublic interface UserRoleMapper {\n\t\n\tList<Role> findByUserName(String userName);\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/handler/GlobalExceptionHandler.java",
    "content": "package com.springboot.handler;\n\nimport org.apache.shiro.authz.AuthorizationException;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\n\n\n@ControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\t\n\t@ExceptionHandler(value = AuthorizationException.class)\n\tpublic String handleAuthorizationException() {\n\t\treturn \"403\";\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/pojo/Permission.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Permission implements Serializable{\n\n\tprivate static final long serialVersionUID = 7160557680614732403L;\n\tprivate Integer id;\n\tprivate String url;\n\tprivate String name;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/pojo/Role.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Role implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -227437593919820521L;\n\tprivate Integer id;\n\tprivate String name;\n\tprivate String memo;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getMemo() {\n\t\treturn memo;\n\t}\n\tpublic void setMemo(String memo) {\n\t\tthis.memo = memo;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.dao.UserPermissionMapper;\nimport com.springboot.dao.UserRoleMapper;\nimport com.springboot.pojo.Permission;\nimport com.springboot.pojo.Role;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\t@Autowired\n\tprivate UserRoleMapper userRoleMapper;\n\t@Autowired\n\tprivate UserPermissionMapper userPermissionMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tString userName = user.getUserName();\n\n\t\tSystem.out.println(\"用户\" + userName + \"获取权限-----ShiroRealm.doGetAuthorizationInfo\");\n\t\tSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n\t\t// 获取用户角色集\n\t\tList<Role> roleList = userRoleMapper.findByUserName(userName);\n\t\tSet<String> roleSet = new HashSet<String>();\n\t\tfor (Role r : roleList) {\n\t\t\troleSet.add(r.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setRoles(roleSet);\n\n\t\t// 获取用户权限集\n\t\tList<Permission> permissionList = userPermissionMapper.findByUserName(userName);\n\t\tSet<String> permissionSet = new HashSet<String>();\n\t\tfor (Permission p : permissionList) {\n\t\t\tpermissionSet.add(p.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setStringPermissions(permissionSet);\n\t\treturn simpleAuthorizationInfo;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"tester\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n    \n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n    \n     "
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/config/shiro-ehcache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://ehcache.org/ehcache.xsd\"\n    updateCheck=\"false\">\n    <diskStore path=\"java.io.tmpdir/Tmp_EhCache\" />\n    <defaultCache\n        maxElementsInMemory=\"10000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"120\"\n        timeToLiveSeconds=\"120\"\n        overflowToDisk=\"false\"\n        diskPersistent=\"false\"\n        diskExpiryThreadIntervalSeconds=\"120\" />\n    \n    <!-- 登录记录缓存锁定1小时 -->\n    <cache \n        name=\"passwordRetryCache\"\n        maxEntriesLocalHeap=\"2000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"3600\"\n        timeToLiveSeconds=\"0\"\n        overflowToDisk=\"false\"\n        statistics=\"true\" />\n</ehcache>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_PERMISSION\n-- ----------------------------\nCREATE TABLE T_PERMISSION (\n   ID NUMBER(10) NOT NULL ,\n   URL VARCHAR2(256 BYTE) NULL ,\n   NAME VARCHAR2(64 BYTE) NULL \n);\nCOMMENT ON COLUMN T_PERMISSION.URL IS 'url地址';\nCOMMENT ON COLUMN T_PERMISSION.NAME IS 'url描述';\n-- ----------------------------\n-- Records of T_PERMISSION\n-- ----------------------------\nINSERT INTO T_PERMISSION VALUES ('1', '/user', 'user:user');\nINSERT INTO T_PERMISSION VALUES ('2', '/user/add', 'user:add');\nINSERT INTO T_PERMISSION VALUES ('3', '/user/delete', 'user:delete');\n-- ----------------------------\n-- Table structure for T_ROLE\n-- ----------------------------\nCREATE TABLE T_ROLE (\n   ID NUMBER NOT NULL ,\n   NAME VARCHAR2(32 BYTE) NULL ,\n   MEMO VARCHAR2(32 BYTE) NULL \n);\nCOMMENT ON COLUMN T_ROLE.NAME IS '角色名称';\nCOMMENT ON COLUMN T_ROLE.MEMO IS '角色描述';\n-- ----------------------------\n-- Records of T_ROLE\n-- ----------------------------\nINSERT INTO T_ROLE VALUES ('1', 'admin', '超级管理员');\nINSERT INTO T_ROLE VALUES ('2', 'test', '测试账户');\n-- ----------------------------\n-- Table structure for T_ROLE_PERMISSION\n-- ----------------------------\nCREATE TABLE T_ROLE_PERMISSION (\n   RID NUMBER(10) NULL ,\n   PID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_ROLE_PERMISSION.RID IS '角色id';\nCOMMENT ON COLUMN T_ROLE_PERMISSION.PID IS '权限id';\n-- ----------------------------\n-- Records of T_ROLE_PERMISSION\n-- ----------------------------\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '2');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '3');\nINSERT INTO T_ROLE_PERMISSION VALUES ('2', '1');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '1');\n-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Table structure for T_USER_ROLE\n-- ----------------------------\nCREATE TABLE T_USER_ROLE (\n   USER_ID NUMBER(10) NULL ,\n   RID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_USER_ROLE.USER_ID IS '用户id';\nCOMMENT ON COLUMN T_USER_ROLE.RID IS '角色id';\n-- ----------------------------\n-- Records of T_USER_ROLE\n-- ----------------------------\nINSERT INTO T_USER_ROLE VALUES ('1', '1');\nINSERT INTO T_USER_ROLE VALUES ('2', '2');"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/mapper/UserPermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserPermissionMapper\">\n\n<resultMap type=\"com.springboot.pojo.Permission\" id=\"permission\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"url\" property=\"url\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"permission\">\n\tselect p.id,p.url,p.name from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\tleft join t_role_permission rp on(rp.rid = r.id) \n\tleft join t_permission p on(p.id = rp.pid ) \n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/mapper/UserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserRoleMapper\">\n\n<resultMap type=\"com.springboot.pojo.Role\" id=\"role\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"memo\" property=\"memo\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"role\">\n\tselect r.id,r.name,r.memo from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/templates/403.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>暂无权限</title>\n</head>\n<body>\n\t<p>您没有权限访问该资源！！</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html \n\txmlns:th=\"http://www.thymeleaf.org\" \n\txmlns:shiro=\"http://www.pollix.at/thymeleaf/shiro\" >\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<style>\n\tdiv {\n\t\tborder: 1px dashed #ddd;\n\t\tpadding: 10px;\n\t\tmargin: 10px 10px 10px 0px;\n\t}\n</style>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<p shiro:hasRole=\"admin\">你的角色为超级管理员</p>\n\t<p shiro:hasRole=\"test\">你的角色为测试账户</p>\n\t<div>\n\t\t<a shiro:hasPermission=\"user:user\" th:href=\"@{/user/list}\">获取用户信息</a>\n\t\t<a shiro:hasPermission=\"user:add\" th:href=\"@{/user/add}\">新增用户</a>\n\t\t<a shiro:hasPermission=\"user:delete\" th:href=\"@{/user/delete}\">删除用户</a>\n\t</div>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "16.Spring-Boot-Shiro-Thymeleaf-Tag/src/main/resources/templates/user.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>[[${value}]]</title>\n</head>\n<body>\n\t<p>[[${value}]]</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>demo</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.mybatis.spring.boot</groupId>\n\t\t    <artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t    <version>1.3.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro-spring -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-spring</artifactId>\n\t\t    <version>1.4.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- shiro ehcache -->\n\t\t<dependency>\n\t\t    <groupId>org.apache.shiro</groupId>\n\t\t    <artifactId>shiro-ehcache</artifactId>\n\t\t    <version>1.3.2</version>\n\t\t</dependency>\n\t\t<!-- ehchache -->\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-cache</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t    <groupId>net.sf.ehcache</groupId>\n\t\t    <artifactId>ehcache</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->\n\t\t<dependency>\n\t\t    <groupId>com.github.theborakompanioni</groupId>\n\t\t    <artifactId>thymeleaf-extras-shiro</artifactId>\n\t\t    <version>2.0.0</version>\n\t\t</dependency>\n\t\t\n\t\t\n\t\t<!-- oracle驱动 -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t\n\t\t<!-- druid数据源驱动 -->\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/config/ShiroConfig.java",
    "content": "package com.springboot.config;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\n\nimport org.apache.shiro.cache.ehcache.EhCacheManager;\nimport org.apache.shiro.codec.Base64;\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.session.SessionListener;\nimport org.apache.shiro.session.mgt.SessionManager;\nimport org.apache.shiro.session.mgt.eis.MemorySessionDAO;\nimport org.apache.shiro.session.mgt.eis.SessionDAO;\nimport org.apache.shiro.spring.LifecycleBeanPostProcessor;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.CookieRememberMeManager;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.apache.shiro.web.servlet.SimpleCookie;\nimport org.apache.shiro.web.session.mgt.DefaultWebSessionManager;\nimport org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.DependsOn;\n\nimport com.springboot.listener.ShiroSessionListener;\nimport com.springboot.shiro.ShiroRealm;\n\nimport at.pollux.thymeleaf.shiro.dialect.ShiroDialect;\n\n@Configuration\npublic class ShiroConfig {\n\t\n\t@Bean\n\tpublic EhCacheManager getEhCacheManager() {\n\t    EhCacheManager em = new EhCacheManager();\n\t    em.setCacheManagerConfigFile(\"classpath:config/shiro-ehcache.xml\");\n\t    return em;\n\t}\n\t\n\t@Bean\n\tpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n\t\tShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n\t\tshiroFilterFactoryBean.setSecurityManager(securityManager);\n\t\tshiroFilterFactoryBean.setLoginUrl(\"/login\");\n\t\tshiroFilterFactoryBean.setSuccessUrl(\"/index\");\n\t\tshiroFilterFactoryBean.setUnauthorizedUrl(\"/403\");\n\t\tLinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n\t\t\n\t\tfilterChainDefinitionMap.put(\"/css/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/js/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/fonts/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/img/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/druid/**\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/logout\", \"logout\");\n\t\tfilterChainDefinitionMap.put(\"/\", \"anon\");\n\t\tfilterChainDefinitionMap.put(\"/**\", \"user\");\n\t\t\n\t\tshiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n\t\t\n\t\treturn shiroFilterFactoryBean;\n\t}\n \n\t@Bean  \n    public SecurityManager securityManager(){  \n       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();\n       securityManager.setRealm(shiroRealm());\n       securityManager.setRememberMeManager(rememberMeManager());\n       securityManager.setCacheManager(getEhCacheManager());\n       securityManager.setSessionManager(sessionManager());\n       return securityManager;  \n    }  \n\t\n\t@Bean(name = \"lifecycleBeanPostProcessor\")\n    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {\n        return new LifecycleBeanPostProcessor();\n    }\n\t\n\t@Bean  \n    public ShiroRealm shiroRealm(){  \n       ShiroRealm shiroRealm = new ShiroRealm();  \n       return shiroRealm;  \n    }  \n\t\n\tpublic SimpleCookie rememberMeCookie() {\n\t\tSimpleCookie cookie = new SimpleCookie(\"rememberMe\");\n\t\tcookie.setMaxAge(86400);\n\t\treturn cookie;\n\t}\n\t\n\tpublic CookieRememberMeManager rememberMeManager() {\n\t\tCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();\n\t\tcookieRememberMeManager.setCookie(rememberMeCookie());\n\t\tcookieRememberMeManager.setCipherKey(Base64.decode(\"4AvVhmFLUs0KTA3Kprsdag==\"));\n\t\treturn cookieRememberMeManager;\n\t}\n\t\n\t@Bean\n    @DependsOn({\"lifecycleBeanPostProcessor\"})\n    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {\n        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();\n        advisorAutoProxyCreator.setProxyTargetClass(true);\n        return advisorAutoProxyCreator;\n    }\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n    \n    @Bean\n\tpublic ShiroDialect shiroDialect() {\n\t\treturn new ShiroDialect();\n\t}\n    \n\t@Bean\n\tpublic SessionDAO sessionDAO() {\n\t\tMemorySessionDAO sessionDAO = new MemorySessionDAO();\n\t\treturn sessionDAO;\n\t}\n\n\t@Bean\n\tpublic SessionManager sessionManager() {\n\t\tDefaultWebSessionManager sessionManager = new DefaultWebSessionManager();\n\t\tCollection<SessionListener> listeners = new ArrayList<SessionListener>();\n\t\tlisteners.add(new ShiroSessionListener());\n\t\tsessionManager.setSessionListeners(listeners);\n\t\tsessionManager.setSessionDAO(sessionDAO());\n\t\treturn sessionManager;\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/controller/LoginController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authc.UsernamePasswordToken;\nimport org.apache.shiro.subject.Subject;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.User;\nimport com.springboot.util.MD5Utils;\n\n@Controller\npublic class LoginController {\n\n\t@GetMapping(\"/login\")\n\tpublic String login() {\n\t\treturn \"login\";\n\t}\n\n\t@PostMapping(\"/login\")\n\t@ResponseBody\n\tpublic ResponseBo login(String username, String password, Boolean rememberMe) {\n\t\tpassword = MD5Utils.encrypt(username, password);\n\t\tUsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);\n\t\tSubject subject = SecurityUtils.getSubject();\n\t\ttry {\n\t\t\tsubject.login(token);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (UnknownAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (IncorrectCredentialsException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (LockedAccountException e) {\n\t\t\treturn ResponseBo.error(e.getMessage());\n\t\t} catch (AuthenticationException e) {\n\t\t\treturn ResponseBo.error(\"认证失败！\");\n\t\t}\n\t}\n\n\t@RequestMapping(\"/\")\n\tpublic String redirectIndex() {\n\t\treturn \"redirect:/index\";\n\t}\n\t\n\t@GetMapping(\"/403\")\n\tpublic String forbid() {\n\t\treturn \"403\";\n\t}\n\n\t@RequestMapping(\"/index\")\n\tpublic String index(Model model) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tmodel.addAttribute(\"user\", user);\n\t\treturn \"index\";\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/controller/SessionController.java",
    "content": "package com.springboot.controller;\n\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.springboot.pojo.ResponseBo;\nimport com.springboot.pojo.UserOnline;\nimport com.springboot.service.SessionService;\n\n\n@Controller\n@RequestMapping(\"/online\")\npublic class SessionController {\n\t\n\t@Autowired\n\tSessionService sessionService;\n\t\n\t@RequestMapping(\"index\")\n\tpublic String online() {\n\t\treturn \"online\";\n\t}\n\n\t@ResponseBody\n\t@RequestMapping(\"list\")\n\tpublic List<UserOnline> list() {\n\t\treturn sessionService.list();\n\t}\n\n\t@ResponseBody\n\t@RequestMapping(\"forceLogout\")\n\tpublic ResponseBo forceLogout(String id) {\n\t\ttry {\n\t\t\tsessionService.forceLogout(id);\n\t\t\treturn ResponseBo.ok();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn ResponseBo.error(\"踢出用户失败\");\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/controller/UserController.java",
    "content": "package com.springboot.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/user\")\npublic class UserController {\n\t\n\t\n\t@RequiresPermissions(\"user:user\")\n\t@RequestMapping(\"list\")\n\tpublic String userList(Model model) {\n\t\tmodel.addAttribute(\"value\", \"获取用户信息\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:add\")\n\t@RequestMapping(\"add\")\n\tpublic String userAdd(Model model) {\n\t\tmodel.addAttribute(\"value\", \"新增用户\");\n\t\treturn \"user\";\n\t}\n\t\n\t@RequiresPermissions(\"user:delete\")\n\t@RequestMapping(\"delete\")\n\tpublic String userDelete(Model model) {\n\t\tmodel.addAttribute(\"value\", \"删除用户\");\n\t\treturn \"user\";\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/dao/UserMapper.java",
    "content": "package com.springboot.dao;\n\nimport org.apache.ibatis.annotations.Mapper;\n\nimport com.springboot.pojo.User;\n\n@Mapper\npublic interface UserMapper {\n\tUser findByUserName(String userName);\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/dao/UserPermissionMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Permission;\n\n@Mapper\npublic interface UserPermissionMapper {\n\t\n\tList<Permission> findByUserName(String userName);\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/dao/UserRoleMapper.java",
    "content": "package com.springboot.dao;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Mapper;\nimport com.springboot.pojo.Role;\n\n@Mapper\npublic interface UserRoleMapper {\n\t\n\tList<Role> findByUserName(String userName);\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/handler/GlobalExceptionHandler.java",
    "content": "package com.springboot.handler;\n\nimport org.apache.shiro.authz.AuthorizationException;\nimport org.apache.shiro.session.ExpiredSessionException;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\n\n\n@ControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\t\n\t@ExceptionHandler(value = AuthorizationException.class)\n\tpublic String handleAuthorizationException() {\n\t\treturn \"403\";\n\t}\n\t\n\t@ExceptionHandler(value = ExpiredSessionException.class )\n\tpublic String handleExpiredSessionException() {\n\t\treturn \"login\";\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/listener/ShiroSessionListener.java",
    "content": "package com.springboot.listener;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.shiro.session.Session;\nimport org.apache.shiro.session.SessionListener;\n\npublic class ShiroSessionListener implements SessionListener{\n\n\tprivate final AtomicInteger sessionCount = new AtomicInteger(0);\n\t\n\t@Override\n\tpublic void onStart(Session session) {\n\t\tsessionCount.incrementAndGet();\n\t}\n\n\t@Override\n\tpublic void onStop(Session session) {\n\t\tsessionCount.decrementAndGet();\n\t\t\n\t}\n\n\t@Override\n\tpublic void onExpiration(Session session) {\n\t\tsessionCount.decrementAndGet();\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/pojo/Permission.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Permission implements Serializable{\n\n\tprivate static final long serialVersionUID = 7160557680614732403L;\n\tprivate Integer id;\n\tprivate String url;\n\tprivate String name;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/pojo/ResponseBo.java",
    "content": "package com.springboot.pojo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ResponseBo extends HashMap<String, Object>{\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ResponseBo() {\n\t\tput(\"code\", 0);\n\t\tput(\"msg\", \"操作成功\");\n\t}\n\n\tpublic static ResponseBo error() {\n\t\treturn error(1, \"操作失败\");\n\t}\n\n\tpublic static ResponseBo error(String msg) {\n\t\treturn error(500, msg);\n\t}\n\n\tpublic static ResponseBo error(int code, String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"code\", code);\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(String msg) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.put(\"msg\", msg);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok(Map<String, Object> map) {\n\t\tResponseBo ResponseBo = new ResponseBo();\n\t\tResponseBo.putAll(map);\n\t\treturn ResponseBo;\n\t}\n\n\tpublic static ResponseBo ok() {\n\t\treturn new ResponseBo();\n\t}\n\n\t@Override\n\tpublic ResponseBo put(String key, Object value) {\n\t\tsuper.put(key, value);\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/pojo/Role.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\n\npublic class Role implements Serializable{\n\t\n\tprivate static final long serialVersionUID = -227437593919820521L;\n\tprivate Integer id;\n\tprivate String name;\n\tprivate String memo;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\tpublic String getMemo() {\n\t\treturn memo;\n\t}\n\tpublic void setMemo(String memo) {\n\t\tthis.memo = memo;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/pojo/User.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable{\n\n\tprivate static final long serialVersionUID = -5440372534300871944L;\n\t\n\tprivate Integer id;\n\tprivate String userName;\n\tprivate String password;\n\tprivate Date createTime;\n\tprivate String status;\n\tpublic Integer getId() {\n\t\treturn id;\n\t}\n\tpublic void setId(Integer id) {\n\t\tthis.id = id;\n\t}\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\tpublic Date getCreateTime() {\n\t\treturn createTime;\n\t}\n\tpublic void setCreateTime(Date createTime) {\n\t\tthis.createTime = createTime;\n\t}\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n    \n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/pojo/UserOnline.java",
    "content": "package com.springboot.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class UserOnline implements Serializable{\n\t\n\tprivate static final long serialVersionUID = 3828664348416633856L;\n\t\n\t// session id\n    private String id;\n    // 用户id\n    private String userId;\n    // 用户名称\n    private String username;\n\t// 用户主机地址\n    private String host;\n    // 用户登录时系统IP\n    private String systemHost;\n    // 状态\n    private String status;\n    // session创建时间\n    private Date startTimestamp;\n    // session最后访问时间\n    private Date lastAccessTime;\n    // 超时时间\n    private Long timeout;\n\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getUserId() {\n\t\treturn userId;\n\t}\n\n\tpublic void setUserId(String userId) {\n\t\tthis.userId = userId;\n\t}\n\n\tpublic String getUsername() {\n\t\treturn username;\n\t}\n\n\tpublic void setUsername(String username) {\n\t\tthis.username = username;\n\t}\n\n\tpublic String getHost() {\n\t\treturn host;\n\t}\n\n\tpublic void setHost(String host) {\n\t\tthis.host = host;\n\t}\n\n\tpublic String getSystemHost() {\n\t\treturn systemHost;\n\t}\n\n\tpublic void setSystemHost(String systemHost) {\n\t\tthis.systemHost = systemHost;\n\t}\n\n\tpublic String getStatus() {\n\t\treturn status;\n\t}\n\n\tpublic void setStatus(String status) {\n\t\tthis.status = status;\n\t}\n\n\tpublic Date getStartTimestamp() {\n\t\treturn startTimestamp;\n\t}\n\n\tpublic void setStartTimestamp(Date startTimestamp) {\n\t\tthis.startTimestamp = startTimestamp;\n\t}\n\n\tpublic Date getLastAccessTime() {\n\t\treturn lastAccessTime;\n\t}\n\n\tpublic void setLastAccessTime(Date lastAccessTime) {\n\t\tthis.lastAccessTime = lastAccessTime;\n\t}\n\n\tpublic Long getTimeout() {\n\t\treturn timeout;\n\t}\n\n\tpublic void setTimeout(Long timeout) {\n\t\tthis.timeout = timeout;\n\t}\n\n    \n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/service/SessionService.java",
    "content": "package com.springboot.service;\n\nimport java.util.List;\n\nimport com.springboot.pojo.UserOnline;\n\npublic interface SessionService {\n\t\n\tList<UserOnline> list();\n\tboolean forceLogout(String sessionId);\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/service/impl/SessionServiceImpl.java",
    "content": "package com.springboot.service.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.shiro.session.Session;\nimport org.apache.shiro.session.mgt.eis.SessionDAO;\nimport org.apache.shiro.subject.SimplePrincipalCollection;\nimport org.apache.shiro.subject.support.DefaultSubjectContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport com.springboot.pojo.User;\nimport com.springboot.pojo.UserOnline;\nimport com.springboot.service.SessionService;\n\n@Service(\"sessionService\")\npublic class SessionServiceImpl implements SessionService {\n\n\t@Autowired\n\tprivate SessionDAO sessionDAO;\n\n\t@Override\n\tpublic List<UserOnline> list() {\n\t\tList<UserOnline> list = new ArrayList<>();\n\t\tCollection<Session> sessions = sessionDAO.getActiveSessions();\n\t\tfor (Session session : sessions) {\n\t\t\tUserOnline userOnline = new UserOnline();\n\t\t\tUser user = new User();\n\t\t\tSimplePrincipalCollection principalCollection = new SimplePrincipalCollection();\n\t\t\tif (session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) {\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tprincipalCollection = (SimplePrincipalCollection) session\n\t\t\t\t\t\t.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);\n\t\t\t\tuser = (User) principalCollection.getPrimaryPrincipal();\n\t\t\t\tuserOnline.setUsername(user.getUserName());\n\t\t\t\tuserOnline.setUserId(user.getId().toString());\n\t\t\t}\n\t\t\tuserOnline.setId((String) session.getId());\n\t\t\tuserOnline.setHost(session.getHost());\n\t\t\tuserOnline.setStartTimestamp(session.getStartTimestamp());\n\t\t\tuserOnline.setLastAccessTime(session.getLastAccessTime());\n\t\t\tLong timeout = session.getTimeout();\n\t\t\tif (timeout == 0l) {\n\t\t\t\tuserOnline.setStatus(\"离线\");\n\t\t\t} else {\n\t\t\t\tuserOnline.setStatus(\"在线\");\n\t\t\t}\n\t\t\tuserOnline.setTimeout(timeout);\n\t\t\tlist.add(userOnline);\n\t\t}\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic boolean forceLogout(String sessionId) {\n\t\tSession session = sessionDAO.readSession(sessionId);\n\t\tsession.setTimeout(0);\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/shiro/ShiroRealm.java",
    "content": "package com.springboot.shiro;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.dao.UserMapper;\nimport com.springboot.dao.UserPermissionMapper;\nimport com.springboot.dao.UserRoleMapper;\nimport com.springboot.pojo.Permission;\nimport com.springboot.pojo.Role;\nimport com.springboot.pojo.User;\n\npublic class ShiroRealm extends AuthorizingRealm {\n\n\t@Autowired\n\tprivate UserMapper userMapper;\n\t@Autowired\n\tprivate UserRoleMapper userRoleMapper;\n\t@Autowired\n\tprivate UserPermissionMapper userPermissionMapper;\n\n\t/**\n\t * 获取用户角色和权限\n\t */\n\t@Override\n\tprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {\n\t\tUser user = (User) SecurityUtils.getSubject().getPrincipal();\n\t\tString userName = user.getUserName();\n\n\t\tSystem.out.println(\"用户\" + userName + \"获取权限-----ShiroRealm.doGetAuthorizationInfo\");\n\t\tSimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n\t\t// 获取用户角色集\n\t\tList<Role> roleList = userRoleMapper.findByUserName(userName);\n\t\tSet<String> roleSet = new HashSet<String>();\n\t\tfor (Role r : roleList) {\n\t\t\troleSet.add(r.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setRoles(roleSet);\n\n\t\t// 获取用户权限集\n\t\tList<Permission> permissionList = userPermissionMapper.findByUserName(userName);\n\t\tSet<String> permissionSet = new HashSet<String>();\n\t\tfor (Permission p : permissionList) {\n\t\t\tpermissionSet.add(p.getName());\n\t\t}\n\t\tsimpleAuthorizationInfo.setStringPermissions(permissionSet);\n\t\treturn simpleAuthorizationInfo;\n\t}\n\n\t/**\n\t * 登录认证\n\t */\n\t@Override\n\tprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {\n\t\tString userName = (String) token.getPrincipal();\n\t\tString password = new String((char[]) token.getCredentials());\n\n\t\tSystem.out.println(\"用户\" + userName + \"认证-----ShiroRealm.doGetAuthenticationInfo\");\n\t\tUser user = userMapper.findByUserName(userName);\n\n\t\tif (user == null) {\n\t\t\tthrow new UnknownAccountException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (!password.equals(user.getPassword())) {\n\t\t\tthrow new IncorrectCredentialsException(\"用户名或密码错误！\");\n\t\t}\n\t\tif (user.getStatus().equals(\"0\")) {\n\t\t\tthrow new LockedAccountException(\"账号已被锁定,请联系管理员！\");\n\t\t}\n\t\tSimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());\n\t\treturn info;\n\t}\n\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/java/com/springboot/util/MD5Utils.java",
    "content": "package com.springboot.util;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\npublic class MD5Utils {\n\tprivate static final String SALT = \"mrbird\";\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\n\tpublic static String encrypt(String username, String pswd) {\n\t\tString newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t\treturn newPassword;\n\t}\n\tpublic static void main(String[] args) {\n\t\t\n\t\tSystem.out.println(MD5Utils.encrypt(\"tester\", \"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源 \n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: scott\n      password: 6742530   \n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        # IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true \n\n  thymeleaf:\n    cache: false\n    \n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.pojo\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n    \n     "
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/config/shiro-ehcache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"http://ehcache.org/ehcache.xsd\"\n    updateCheck=\"false\">\n    <diskStore path=\"java.io.tmpdir/Tmp_EhCache\" />\n    <defaultCache\n        maxElementsInMemory=\"10000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"120\"\n        timeToLiveSeconds=\"120\"\n        overflowToDisk=\"false\"\n        diskPersistent=\"false\"\n        diskExpiryThreadIntervalSeconds=\"120\" />\n    \n    <!-- 登录记录缓存锁定1小时 -->\n    <cache \n        name=\"passwordRetryCache\"\n        maxEntriesLocalHeap=\"2000\"\n        eternal=\"false\"\n        timeToIdleSeconds=\"3600\"\n        timeToLiveSeconds=\"0\"\n        overflowToDisk=\"false\"\n        statistics=\"true\" />\n</ehcache>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_PERMISSION\n-- ----------------------------\nCREATE TABLE T_PERMISSION (\n   ID NUMBER(10) NOT NULL ,\n   URL VARCHAR2(256 BYTE) NULL ,\n   NAME VARCHAR2(64 BYTE) NULL \n);\nCOMMENT ON COLUMN T_PERMISSION.URL IS 'url地址';\nCOMMENT ON COLUMN T_PERMISSION.NAME IS 'url描述';\n-- ----------------------------\n-- Records of T_PERMISSION\n-- ----------------------------\nINSERT INTO T_PERMISSION VALUES ('1', '/user', 'user:user');\nINSERT INTO T_PERMISSION VALUES ('2', '/user/add', 'user:add');\nINSERT INTO T_PERMISSION VALUES ('3', '/user/delete', 'user:delete');\n-- ----------------------------\n-- Table structure for T_ROLE\n-- ----------------------------\nCREATE TABLE T_ROLE (\n   ID NUMBER NOT NULL ,\n   NAME VARCHAR2(32 BYTE) NULL ,\n   MEMO VARCHAR2(32 BYTE) NULL \n);\nCOMMENT ON COLUMN T_ROLE.NAME IS '角色名称';\nCOMMENT ON COLUMN T_ROLE.MEMO IS '角色描述';\n-- ----------------------------\n-- Records of T_ROLE\n-- ----------------------------\nINSERT INTO T_ROLE VALUES ('1', 'admin', '超级管理员');\nINSERT INTO T_ROLE VALUES ('2', 'test', '测试账户');\n-- ----------------------------\n-- Table structure for T_ROLE_PERMISSION\n-- ----------------------------\nCREATE TABLE T_ROLE_PERMISSION (\n   RID NUMBER(10) NULL ,\n   PID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_ROLE_PERMISSION.RID IS '角色id';\nCOMMENT ON COLUMN T_ROLE_PERMISSION.PID IS '权限id';\n-- ----------------------------\n-- Records of T_ROLE_PERMISSION\n-- ----------------------------\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '2');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '3');\nINSERT INTO T_ROLE_PERMISSION VALUES ('2', '1');\nINSERT INTO T_ROLE_PERMISSION VALUES ('1', '1');\n-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n-- ----------------------------\n-- Table structure for T_USER_ROLE\n-- ----------------------------\nCREATE TABLE T_USER_ROLE (\n   USER_ID NUMBER(10) NULL ,\n   RID NUMBER(10) NULL \n);\nCOMMENT ON COLUMN T_USER_ROLE.USER_ID IS '用户id';\nCOMMENT ON COLUMN T_USER_ROLE.RID IS '角色id';\n-- ----------------------------\n-- Records of T_USER_ROLE\n-- ----------------------------\nINSERT INTO T_USER_ROLE VALUES ('1', '1');\nINSERT INTO T_USER_ROLE VALUES ('2', '2');"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserMapper\">\n\n<resultMap type=\"com.springboot.pojo.User\" id=\"User\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"username\" property=\"userName\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"passwd\" property=\"password\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"create_time\" property=\"createTime\" javaType=\"java.util.Date\" jdbcType=\"DATE\"/>\n   <id column=\"status\" property=\"status\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"User\">\n\tselect * from t_user where username = #{userName}\n</select>\n\n</mapper>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/mapper/UserPermissionMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserPermissionMapper\">\n\n<resultMap type=\"com.springboot.pojo.Permission\" id=\"permission\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"url\" property=\"url\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"permission\">\n\tselect p.id,p.url,p.name from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\tleft join t_role_permission rp on(rp.rid = r.id) \n\tleft join t_permission p on(p.id = rp.pid ) \n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/mapper/UserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.dao.UserRoleMapper\">\n\n<resultMap type=\"com.springboot.pojo.Role\" id=\"role\">\n   <id column=\"id\" property=\"id\" javaType=\"java.lang.Integer\" jdbcType=\"NUMERIC\"/>\n   <id column=\"name\" property=\"name\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n   <id column=\"memo\" property=\"memo\" javaType=\"java.lang.String\" jdbcType=\"VARCHAR\"/>\n</resultMap>\n\n<select id=\"findByUserName\" resultMap=\"role\">\n\tselect r.id,r.name,r.memo from t_role r\n\tleft join t_user_role ur on(r.id = ur.rid) \n\tleft join t_user u on(u.id = ur.user_id)\n\twhere u.username = #{userName}\n</select>\n</mapper>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/static/css/login.css",
    "content": ".login-page {\n  width: 360px;\n  padding: 8% 0 0;\n  margin: auto;\n}\n.form {\n  position: relative;\n  z-index: 1;\n  background: #ffffff;\n  max-width: 360px;\n  margin: 0 auto 100px;\n  padding: 45px;\n  text-align: center;\n  box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n  outline: 0;\n  background: #f2f2f2;\n  width: 100%;\n  border: 0;\n  margin: 0 0 15px;\n  padding: 15px;\n  box-sizing: border-box;\n  font-size: 14px;\n}\n.form button {\n  text-transform: uppercase;\n  outline: 0;\n  background: #4caf50;\n  width: 100%;\n  border: 0;\n  padding: 15px;\n  color: #ffffff;\n  font-size: 14px;\n  -webkit-transition: all 0.3 ease;\n  transition: all 0.3 ease;\n  cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n  background: #43a047;\n}\n.form .message {\n  margin: 15px 0 0;\n  color: #b3b3b3;\n  font-size: 12px;\n}\n.form .message a {\n  color: #4caf50;\n  text-decoration: none;\n}\n.form .register-form {\n  display: none;\n}\n.form p {\n\ttext-align: left;\n\tmargin: 0;\n\tfont-size: 13px;\n}\n.form p input {\n\twidth: auto;\n\tmargin-right: 10px;\t\n}\t\n.container {\n  position: relative;\n  z-index: 1;\n  max-width: 300px;\n  margin: 0 auto;\n}\n.container:before,\n.container:after {\n  content: \"\";\n  display: block;\n  clear: both;\n}\n.container .info {\n  margin: 50px auto;\n  text-align: center;\n}\n.container .info h1 {\n  margin: 0 0 15px;\n  padding: 0;\n  font-size: 36px;\n  font-weight: 300;\n  color: #1a1a1a;\n}\n.container .info span {\n  color: #4d4d4d;\n  font-size: 12px;\n}\n.container .info span a {\n  color: #000000;\n  text-decoration: none;\n}\n.container .info span .fa {\n  color: #ef3b3a;\n}\nbody {\n  background: #76b852; /* fallback for old browsers */\n  background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n  background: -moz-linear-gradient(right, #76b852, #8dc26f);\n  background: -o-linear-gradient(right, #76b852, #8dc26f);\n  background: linear-gradient(to left, #76b852, #8dc26f);\n  font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/static/js/dateFormat.js",
    "content": "Date.prototype.Format = function (fmt) { \n    var o = {\n        \"M+\": this.getMonth() + 1, \n        \"d+\": this.getDate(), \n        \"h+\": this.getHours(), \n        \"m+\": this.getMinutes(),\n        \"s+\": this.getSeconds(), \n        \"q+\": Math.floor((this.getMonth() + 3) / 3), \n        \"S\": this.getMilliseconds()\n    };\n    if (/(y+)/.test(fmt)){\n        fmt = fmt.replace(RegExp.$1, (this.getFullYear() + \"\")\n            .substr(4 - RegExp.$1.length));\n    }\n    for (var k in o){\n        if (new RegExp(\"(\" + k + \")\").test(fmt)){\n            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ?\n             (o[k]) : ((\"00\" + o[k]).substr((\"\" + o[k]).length)));\n        } \n    }\n    return fmt;\n}"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/templates/403.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>暂无权限</title>\n</head>\n<body>\n\t<p>您没有权限访问该资源！！</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html \n\txmlns:th=\"http://www.thymeleaf.org\" \n\txmlns:shiro=\"http://www.pollix.at/thymeleaf/shiro\" >\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>首页</title>\n</head>\n<style>\n\tdiv {\n\t\tborder: 1px dashed #ddd;\n\t\tpadding: 10px;\n\t\tmargin: 10px 10px 10px 0px;\n\t}\n</style>\n<body>\n\t<p>你好！[[${user.userName}]]</p>\n\t<p shiro:hasRole=\"admin\">你的角色为超级管理员</p>\n\t<p shiro:hasRole=\"test\">你的角色为测试账户</p>\n\t<div>\n\t\t<a shiro:hasPermission=\"user:user\" th:href=\"@{/user/list}\">获取用户信息</a>\n\t\t<a shiro:hasPermission=\"user:add\" th:href=\"@{/user/add}\">新增用户</a>\n\t\t<a shiro:hasPermission=\"user:delete\" th:href=\"@{/user/delete}\">删除用户</a>\n\t</div>\n\t<a shiro:hasRole=\"admin\" th:href=\"@{/online/index}\">在线用户管理</a>\n\t<a th:href=\"@{/logout}\">注销</a>\n</body>\n</html>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/templates/login.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>登录</title>\n\t<link rel=\"stylesheet\" th:href=\"@{/css/login.css}\" type=\"text/css\">\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n</head>\n<body>\n\t<div class=\"login-page\">\n\t  <div class=\"form\">\n\t      <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n\t      <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n\t      <p><input type=\"checkbox\" name=\"rememberMe\" />记住我</p>\n\t      <button onclick=\"login()\">登录</button>\n\t  </div>\n\t</div>\n</body>\n<script th:inline=\"javascript\"> \n\tvar ctx = [[@{/}]];\n    function login() {\n    \tvar username = $(\"input[name='username']\").val();\n    \tvar password = $(\"input[name='password']\").val();\n    \tvar rememberMe =$(\"input[name='rememberMe']\").is(':checked');\n        $.ajax({\n            type: \"post\",\n            url: ctx + \"login\",\n            data: {\"username\": username,\"password\": password,\"rememberMe\": rememberMe},\n            dataType: \"json\",\n            success: function (r) {\n                if (r.code == 0) {\n                    location.href = ctx + 'index';\n                } else {\n\t\t\t\t\talert(r.msg);\n                }\n            }\n        });\n    }\n</script>\n</html>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/templates/online.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>在线用户管理</title>\n\t<script th:src=\"@{/js/jquery-1.11.1.min.js}\"></script>\n\t<script th:src=\"@{/js/dateFormat.js}\"></script>\n</head>\n<style>\n\ttable {\n\t\tmargin: 20px 40px 20px 0px;\n\t\twidth: 100%;\n\t\tborder-collapse: collapse;\n\t\tborder-spacing: 0;\n\t\ttable-layout: automatic;\n\t\tword-wrap: break-all\n\t}\n\ttable>tbody>tr:nth-of-type(odd) {\n\t\tbackground-color: #F7F7F7\n\t}\n\t\n\tth, td {\n\t\tpadding: 8px;\n\t\ttext-align: left;\n\t\tvertical-align: middle;\n\t\tfont-weight: normal;\n\t\tfont-size: 12px;\n\t\tborder-bottom: 1px solid #fff;\n\t}\n\t\n\tth {\n\t\tpadding-bottom: 10px;\n\t    color: #fff;\n\t    font-weight: 700;\n\t    background: rgba(66, 185, 131, .9)\n\t}\n\t\n\ttd {\n\t\tborder-bottom-width: 1px\n\t}\n\t\n</style>\n<body>\n\t<h3>在线用户数：<span id=\"onlineCount\"></span></h3>\n\t<table>\n\t\t<tr>\n\t\t\t<th>序号</th>\n\t\t\t<th>用户名称</th>\n\t\t\t<th>登录时间</th>\n\t\t\t<th>最后访问时间</th>\n\t\t\t<th>主机</th>\n\t\t\t<th>状态</th>\n\t\t\t<th>操作</th>\n\t\t</tr>\n\t</table>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n<script th:inline=\"javascript\">\n\tvar ctx = [[@{/}]];\n\t$.get(ctx + \"online/list\", {}, function(r){\n\t\tconsole.log(r);\n\t\tvar length = r.length;\n\t\t$(\"#onlineCount\").text(length);\n\t\tvar html = \"\";\n\t\tfor(var i = 0; i < length; i++){\n\t\t\thtml += \"<tr>\"\n\t\t\t\t + \"<td>\" + (i+1) + \"</td>\"\n\t\t\t     + \"<td>\" + r[i].username + \"</td>\"\n\t\t\t     + \"<td>\" + new Date(r[i].startTimestamp).Format(\"yyyy-MM-dd hh:mm:ss\") + \"</td>\"\n\t\t\t     + \"<td>\" + new Date(r[i].lastAccessTime).Format(\"yyyy-MM-dd hh:mm:ss\") + \"</td>\"\n\t\t\t     + \"<td>\" + r[i].host + \"</td>\"\n\t\t\t     + \"<td>\" + r[i].status + \"</td>\"\n\t\t\t     + \"<td><a href='#' onclick='offline(\\\"\" + r[i].id + \"\\\",\\\"\" + r[i].status +\"\\\")'>下线</a></td>\"\n\t\t\t     + \"</tr>\";\n\t\t}\n\t\t$(\"table\").append(html);\n\t},\"json\");\n\t\n\tfunction offline(id,status){\n\t\tif(status == \"离线\"){\n\t\t\talert(\"该用户已是离线状态！！\");\n\t\t\treturn;\n\t\t}\n\t\t$.get(ctx + \"online/forceLogout\", {\"id\": id}, function(r){\n\t\t\tif (r.code == 0) {\n\t\t\t\talert('该用户已强制下线！');\n                location.href = ctx + 'online/index';\n            } else {\n\t\t\t\talert(r.msg);\n            }\n\t\t},\"json\");\n\t}\n</script>\n</html>"
  },
  {
    "path": "17.Spring-Boot-Shiro-Session/src/main/resources/templates/user.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>[[${value}]]</title>\n</head>\n<body>\n\t<p>[[${value}]]</p>\n\t<a th:href=\"@{/index}\">返回</a>\n</body>\n</html>"
  },
  {
    "path": "18.Spring-Boot-Jackson/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.example</groupId>\n\t<artifactId>Spring-Boot-Jackson</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/DemoApplication.java",
    "content": "package com.example;\n\nimport java.io.IOException;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) throws JsonProcessingException, IOException {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\n\t}\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/config/JacksonConfig.java",
    "content": "package com.example.config;\n\nimport java.text.SimpleDateFormat;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@Configuration\npublic class JacksonConfig {\n\t@Bean\n\tpublic ObjectMapper getObjectMapper(){\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tmapper.setDateFormat(new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\"));\n\t\treturn mapper;\n\t}\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/config/UserDeserializer.java",
    "content": "package com.example.config;\n\nimport java.io.IOException;\n\nimport com.example.pojo.User;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.JsonDeserializer;\nimport com.fasterxml.jackson.databind.JsonNode;\n\npublic class UserDeserializer extends JsonDeserializer<User> {\n\n\t@Override\n\tpublic User deserialize(JsonParser parser, DeserializationContext context)\n\t\t\tthrows IOException, JsonProcessingException {\n\t\tJsonNode node = parser.getCodec().readTree(parser);\n\t\tString userName = node.get(\"user-name\").asText();\n\t\tUser user = new User();\n\t\tuser.setUserName(userName);\n\t\treturn user;\n\t}\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/config/UserSerializer.java",
    "content": "package com.example.config;\n\nimport java.io.IOException;\n\nimport com.example.pojo.User;\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\n\npublic class UserSerializer extends JsonSerializer<User> {\n\n\t@Override\n\tpublic void serialize(User user, JsonGenerator generator, SerializerProvider provider)\n\t\t\tthrows IOException, JsonProcessingException {\n\t\tgenerator.writeStartObject();\n\t\tgenerator.writeStringField(\"user-name\", user.getUserName());\n\t\tgenerator.writeEndObject();\n\t}\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/controller/TestJsonController.java",
    "content": "package com.example.controller;\n\nimport java.io.IOException;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.example.pojo.User;\nimport com.fasterxml.jackson.annotation.JsonView;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@Controller\npublic class TestJsonController {\n\n\t@Autowired\n\tObjectMapper mapper;\n\n\t@JsonView(User.AllUserFieldView.class)\n\t@RequestMapping(\"getuser\")\n\t@ResponseBody\n\tpublic User getUser() {\n\t\tUser user = new User();\n\t\tuser.setUserName(\"mrbird\");\n\t\tuser.setAge(26);\n\t\tuser.setPassword(\"123456\");\n\t\tuser.setBirthday(new Date());\n\t\treturn user;\n\t}\n\n\t@RequestMapping(\"serialization\")\n\t@ResponseBody\n\tpublic String serialization() {\n\t\ttry {\n\t\t\tUser user = new User();\n\t\t\tuser.setUserName(\"mrbird\");\n\t\t\tuser.setBirthday(new Date());\n\t\t\tString str = mapper.writeValueAsString(user);\n\t\t\treturn str;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@RequestMapping(\"readjsonstring\")\n\t@ResponseBody\n\tpublic String readJsonString() {\n\t\ttry {\n\t\t\tString json = \"{\\\"name\\\":\\\"mrbird\\\",\\\"age\\\":26}\";\n\t\t\tJsonNode node = this.mapper.readTree(json);\n\t\t\tString name = node.get(\"name\").asText();\n\t\t\tint age = node.get(\"age\").asInt();\n\t\t\treturn name + \" \" + age;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@RequestMapping(\"readjsonasobject\")\n\t@ResponseBody\n\tpublic String readJsonAsObject() {\n\t\ttry {\n\t\t\tString json = \"{\\\"userName\\\":\\\"mrbird\\\"}\";\n\t\t\tUser user = mapper.readValue(json, User.class);\n\t\t\tString name = user.getUserName();\n\t\t\treturn name;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@RequestMapping(\"formatobjecttojsonstring\")\n\t@ResponseBody\n\tpublic String formatObjectToJsonString() {\n\t\ttry {\n\t\t\tUser user = new User();\n\t\t\tuser.setUserName(\"mrbird\");\n\t\t\tuser.setAge(26);\n\t\t\tuser.setPassword(\"123456\");\n\t\t\tuser.setBirthday(new Date());\n\t\t\tString jsonStr = mapper.writeValueAsString(user);\n\t\t\treturn jsonStr;\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n\n\t@RequestMapping(\"updateuser\")\n\t@ResponseBody\n\tpublic int updateUser(@RequestBody List<User> list) {\n\t\treturn list.size();\n\t}\n\n\t@RequestMapping(\"customize\")\n\t@ResponseBody\n\tpublic String customize() throws JsonParseException, JsonMappingException, IOException {\n\t\tString jsonStr = \"[{\\\"userName\\\":\\\"mrbird\\\",\\\"age\\\":26},{\\\"userName\\\":\\\"scott\\\",\\\"age\\\":27}]\";\n\t\tJavaType type = mapper.getTypeFactory().constructParametricType(List.class, User.class);\n\t\tList<User> list = mapper.readValue(jsonStr, type);\n\t\tString msg = \"\";\n\t\tfor (User user : list) {\n\t\t\tmsg += user.getUserName();\n\t\t}\n\t\treturn msg;\n\t}\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/java/com/example/pojo/User.java",
    "content": "package com.example.pojo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.example.config.UserDeserializer;\nimport com.example.config.UserSerializer;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.annotation.JsonView;\nimport com.fasterxml.jackson.databind.PropertyNamingStrategy;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonNaming;\nimport com.fasterxml.jackson.databind.annotation.JsonSerialize;\n\n//@JsonIgnoreProperties({ \"password\", \"age\" })\n//@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)\n//@JsonSerialize(using = UserSerializer.class)\n//@JsonDeserialize (using = UserDeserializer.class)\npublic class User implements Serializable {\n\n\tprivate static final long serialVersionUID = 6222176558369919436L;\n\n\tpublic interface UserNameView {\n\t};\n\n\tpublic interface AllUserFieldView extends UserNameView {\n\t};\n\n\t@JsonView(UserNameView.class)\n\tprivate String userName;\n\t\n\t@JsonView(AllUserFieldView.class)\n\tprivate int age;\n\n\t// @JsonIgnore\n\t@JsonView(AllUserFieldView.class)\n\tprivate String password;\n\n\t// @JsonProperty(\"bth\")\n\t// @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n\t@JsonView(AllUserFieldView.class)\n\tprivate Date birthday;\n\n\tpublic String getUserName() {\n\t\treturn userName;\n\t}\n\n\tpublic void setUserName(String userName) {\n\t\tthis.userName = userName;\n\t}\n\n\tpublic int getAge() {\n\t\treturn age;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic void setAge(int age) {\n\t\tthis.age = age;\n\t}\n\n\tpublic Date getBirthday() {\n\t\treturn birthday;\n\t}\n\n\tpublic void setBirthday(Date birthday) {\n\t\tthis.birthday = birthday;\n\t}\n\n}\n"
  },
  {
    "path": "18.Spring-Boot-Jackson/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "18.Spring-Boot-Jackson/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>demo.springboot</groupId>\n\t<artifactId>Spring-Boot-Testing</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>test</name>\n\t<description>Demo project for Spring Boot test</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<!--mybatis-->\n\t\t<dependency>\n\t\t\t<groupId>org.mybatis.spring.boot</groupId>\n\t\t\t<artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t\t<version>1.3.1</version>\n\t\t</dependency>\n\t\t<!--通用mapper-->\n\t\t<dependency>\n\t\t\t<groupId>tk.mybatis</groupId>\n\t\t\t<artifactId>mapper-spring-boot-starter</artifactId>\n\t\t\t<version>1.1.5</version>\n\t\t</dependency>\n\t\t<!--pagehelper 分页插件-->\n\t\t<dependency>\n\t\t\t<groupId>com.github.pagehelper</groupId>\n\t\t\t<artifactId>pagehelper-spring-boot-starter</artifactId>\n\t\t\t<version>1.2.3</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/TestApplication.java",
    "content": "package demo.springboot.test;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n@SpringBootApplication\n@EnableTransactionManagement\n@MapperScan(\"demo.springboot.test.mapper\")\npublic class TestApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(TestApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/config/MyMapper.java",
    "content": "package demo.springboot.test.config;\n\nimport tk.mybatis.mapper.common.Mapper;\nimport tk.mybatis.mapper.common.MySqlMapper;\n\npublic interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {\n\t\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/controller/UserController.java",
    "content": "package demo.springboot.test.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport demo.springboot.test.domain.User;\nimport demo.springboot.test.service.UserService;\n\n@RestController\npublic class UserController {\n\n\t@Autowired\n\tUserService userService;\n\n\t@GetMapping(\"user/{userName}\")\n\tpublic User getUserByName(@PathVariable(value = \"userName\") String userName) {\n\t\treturn this.userService.findByName(userName);\n\t}\n\n\t@PostMapping(\"user/save\")\n\tpublic void saveUser(@RequestBody User user) {\n\t\tthis.userService.saveUser(user);\n\t}\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/domain/User.java",
    "content": "package demo.springboot.test.domain;\n\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\n@Table(name = \"T_USER\")\npublic class User {\n    @Id\n    @Column(name = \"USER_ID\")\n    private Long id;\n\n    @Column(name = \"USERNAME\")\n    private String username;\n\n    @Column(name = \"PASSWORD\")\n    private String passwd;\n\n    @Column(name = \"CRATE_TIME\")\n    private Date createTime;\n\n    @Column(name = \"STATUS\")\n    private String status;\n\n    /**\n     * @return ID\n     */\n    public Long getId() {\n        return id;\n    }\n\n    /**\n     * @param id\n     */\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    /**\n     * @return USERNAME\n     */\n    public String getUsername() {\n        return username;\n    }\n\n    /**\n     * @param username\n     */\n    public void setUsername(String username) {\n        this.username = username == null ? null : username.trim();\n    }\n\n    /**\n     * @return PASSWD\n     */\n    public String getPasswd() {\n        return passwd;\n    }\n\n    /**\n     * @param passwd\n     */\n    public void setPasswd(String passwd) {\n        this.passwd = passwd == null ? null : passwd.trim();\n    }\n\n    /**\n     * @return CREATE_TIME\n     */\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    /**\n     * @param createTime\n     */\n    public void setCreateTime(Date createTime) {\n        this.createTime = createTime;\n    }\n\n    /**\n     * @return STATUS\n     */\n    public String getStatus() {\n        return status;\n    }\n\n    /**\n     * @param status\n     */\n    public void setStatus(String status) {\n        this.status = status == null ? null : status.trim();\n    }\n}"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/mapper/SeqenceMapper.java",
    "content": "package demo.springboot.test.mapper;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\npublic interface SeqenceMapper {\n\t@Select(\"select ${seqName}.nextval from dual\")\n\tLong getSequence(@Param(\"seqName\") String seqName);\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/mapper/UserMapper.java",
    "content": "package demo.springboot.test.mapper;\n\nimport demo.springboot.test.config.MyMapper;\nimport demo.springboot.test.domain.User;\n\npublic interface UserMapper extends MyMapper<User> {\n}"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/service/IService.java",
    "content": "package demo.springboot.test.service;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic interface IService<T> {\n\n\tLong getSequence(@Param(\"seqName\") String seqName);\n\t\n\tList<T> selectAll();\n\t\n    T selectByKey(Object key);\n\n    int save(T entity);\n\n    int delete(Object key);\n\n    int updateAll(T entity);\n\n    int updateNotNull(T entity);\n\n    List<T> selectByExample(Object example);\n\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/service/UserService.java",
    "content": "package demo.springboot.test.service;\n\nimport demo.springboot.test.domain.User;\n\npublic interface UserService extends IService<User>{\n\tUser findByName(String userName);\n\t\n\tvoid saveUser(User user);\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/service/impl/BaseService.java",
    "content": "package demo.springboot.test.service.impl;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport demo.springboot.test.mapper.SeqenceMapper;\nimport demo.springboot.test.service.IService;\nimport tk.mybatis.mapper.common.Mapper;\n\npublic abstract class BaseService<T> implements IService<T> {\n\n    @Autowired\n    protected Mapper<T> mapper;\n    @Autowired\n    protected SeqenceMapper seqenceMapper;\n    \n    public Mapper<T> getMapper() {\n        return mapper;\n    }\n    @Override\n    public Long getSequence(@Param(\"seqName\") String seqName){\n    \treturn seqenceMapper.getSequence(seqName);\n    }\n    \n    @Override\n    public List<T> selectAll() {\n        //说明：查询所有数据\n        return mapper.selectAll();\n    }\n    \n    @Override\n    public T selectByKey(Object key) {\n        //说明：根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号\n        return mapper.selectByPrimaryKey(key);\n    }\n\n    @Override\n    public int save(T entity) {\n        //说明：保存一个实体，null的属性也会保存，不会使用数据库默认值\n        return mapper.insert(entity);\n    }\n\n    @Override\n    public int delete(Object key) {\n        //说明：根据主键字段进行删除，方法参数必须包含完整的主键属性\n        return mapper.deleteByPrimaryKey(key);\n    }\n\n    @Override\n    public int updateAll(T entity) {\n        //说明：根据主键更新实体全部字段，null值会被更新\n        return mapper.updateByPrimaryKey(entity);\n    }\n\n    @Override\n    public int updateNotNull(T entity) {\n        //根据主键更新属性不为null的值\n        return mapper.updateByPrimaryKeySelective(entity);\n    }\n\n    @Override\n    public List<T> selectByExample(Object example) {\n        //说明：根据Example条件进行查询\n        //重点：这个查询支持通过Example类指定查询列，通过selectProperties方法指定查询列\n        return mapper.selectByExample(example);\n    }\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/java/demo/springboot/test/service/impl/UserServiceImpl.java",
    "content": "package demo.springboot.test.service.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.springframework.stereotype.Repository;\n\nimport demo.springboot.test.domain.User;\nimport demo.springboot.test.service.UserService;\nimport tk.mybatis.mapper.entity.Example;\n\n@Repository(\"userService\")\npublic class UserServiceImpl extends BaseService<User> implements UserService {\n\n\t@Override\n\tpublic User findByName(String userName) {\n\t\tExample example = new Example(User.class);\n\t\texample.createCriteria().andCondition(\"username=\", userName);\n\t\tList<User> userList = this.selectByExample(example);\n\t\tif (userList.size() != 0)\n\t\t\treturn userList.get(0);\n\t\telse\n\t\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void saveUser(User user) {\n\t\tuser.setId(this.getSequence(\"seq_user\"));\n\t\tuser.setCreateTime(new Date());\n\t\tthis.save(user);\n\t}\n\n}\n"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: test\n      password: 123456\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: demo.springboot.test.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n\nmybatis:\n  config-location: classpath:config/mybatis-config.xml\n  # type-aliases扫描路径\n  type-aliases-package: demo.springboot.test.domain\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n\n\n#mappers 多个接口时逗号隔开\nmapper:\n  mappers: demo.springboot.test.config.MyMapper\n  not-empty: false\n  identity: oracle\n\n#pagehelper\npagehelper: \n  helperDialect: oracle\n  reasonable: true\n  supportMethodsArguments: true\n  params: count=countSql"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/resources/config/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration\nPUBLIC \"-//mybatis.org//DTD Config 3.0//EN\"\n\"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\t<settings>\n\t\t<!--解决插入null的时候报错问题-->\n\t\t<setting name=\"jdbcTypeForNull\" value=\"NULL\"/>\n\t</settings>\n</configuration>"
  },
  {
    "path": "19.Spring-Boot-Testing/src/main/resources/init.sql",
    "content": "-- ----------------------------\n-- Table structure for T_USER\n-- ----------------------------\nCREATE TABLE T_USER (\n   ID NUMBER NOT NULL ,\n   USERNAME VARCHAR2(20 BYTE) NOT NULL ,\n   PASSWD VARCHAR2(128 BYTE) NOT NULL ,\n   CREATE_TIME DATE NULL ,\n   STATUS CHAR(1 BYTE) NOT NULL \n);\nCOMMENT ON COLUMN T_USER.USERNAME IS '用户名';\nCOMMENT ON COLUMN T_USER.PASSWD IS '密码';\nCOMMENT ON COLUMN T_USER.CREATE_TIME IS '创建时间';\nCOMMENT ON COLUMN T_USER.STATUS IS '是否有效 1：有效  0：锁定';\n-- ----------------------------\n-- Records of T_USER\n-- ----------------------------\nINSERT INTO T_USER VALUES ('2', 'tester', '243e29429b340192700677d48c09d992', TO_DATE('2017-12-11 17:20:21', 'YYYY-MM-DD HH24:MI:SS'), '1');\nINSERT INTO T_USER VALUES ('1', 'mrbird', '42ee25d1e43e9f57119a00d0a39e5250', TO_DATE('2017-12-11 10:52:48', 'YYYY-MM-DD HH24:MI:SS'), '1');\n\ncreate sequence seq_user start with 1 INCREMENT by 1;"
  },
  {
    "path": "19.Spring-Boot-Testing/src/test/java/demo/springboot/test/UserControllerTest.java",
    "content": "package demo.springboot.test;\n\nimport javax.servlet.http.Cookie;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.mock.web.MockHttpSession;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.result.MockMvcResultHandlers;\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers;\nimport org.springframework.test.web.servlet.setup.MockMvcBuilders;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.context.WebApplicationContext;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.jayway.jsonpath.JsonPath;\n\nimport demo.springboot.test.domain.User;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserControllerTest {\n\n\tprivate MockMvc mockMvc;\n\tprivate MockHttpSession session;\n\t\n\t@Autowired\n    private WebApplicationContext wac;\n\t\n\t@Autowired\n\tObjectMapper mapper;\n\n\t\n\t@Before\n    public void setupMockMvc(){\n\t\tmockMvc = MockMvcBuilders.webAppContextSetup(wac).build();\n\t\tsession = new MockHttpSession();\n\t\tUser user =new User();\n\t\tuser.setUsername(\"Dopa\");\n\t\tuser.setPasswd(\"ac3af72d9f95161a502fd326865c2f15\");\n\t    session.setAttribute(\"user\",user); \n    }\n\t\n\t@Test\n\t@Transactional\n\tpublic void test() throws Exception {\n//\t\tmockMvc.perform(\n//\t\t\t\t MockMvcRequestBuilders.get(\"/user/{userName}\", \"scott\")\n//\t\t\t\t.contentType(MediaType.APPLICATION_JSON_UTF8))\n//\t\t.andExpect(MockMvcResultMatchers.status().isOk())\n//\t\t.andExpect(MockMvcResultMatchers.jsonPath(\"$.username\").value(\"scott\"))\n//\t\t.andDo(MockMvcResultHandlers.print());\n\t\t\n//\t\tString jsonStr = \"{\\\"username\\\":\\\"Dopa\\\",\\\"passwd\\\":\\\"ac3af72d9f95161a502fd326865c2f15\\\",\\\"status\\\":\\\"1\\\"}\";\n\t\t\n\t\tUser user = new User();\n\t\tuser.setUsername(\"Dopa\");\n\t\tuser.setPasswd(\"ac3af72d9f95161a502fd326865c2f15\");\n\t\tuser.setStatus(\"1\");\n\t\t\n\t\tString userJson = mapper.writeValueAsString(user);\n\t\t\n\t\t\n//\t\tmockMvc.perform(MockMvcRequestBuilders.post(\"/user/save\").content(jsonStr.getBytes()));\n\t\t\n\t\tmockMvc.perform(\n\t\t\t\tMockMvcRequestBuilders.post(\"/user/save\")\n\t\t\t\t.contentType(MediaType.APPLICATION_JSON_UTF8)\n\t\t\t\t.content(userJson.getBytes()))\n\t\t.andExpect(MockMvcResultMatchers.status().isOk())\n\t\t.andDo(MockMvcResultHandlers.print());\n\t\t\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/hello?name={name}\",\"mrbird\"));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.post(\"/user/{id}\", 1));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.fileUpload(\"/fileupload\").file(\"file\", \"文件内容\".getBytes(\"utf-8\")));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/hello\").param(\"message\", \"hello\"));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/hobby/save\").param(\"hobby\", \"sleep\", \"eat\"));\n\t\t\n//\t\tMultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();\n//\t\tparams.add(\"name\", \"mrbird\");\n//\t\tparams.add(\"hobby\", \"sleep\");\n//\t\tparams.add(\"hobby\", \"eat\");\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/hobby/save\").params(params));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/index\").sessionAttr(name, value));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/index\").cookie(new Cookie(name, value)));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/index\").contentType(MediaType.APPLICATION_JSON_UTF8));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/user/{id}\", 1).accept(MediaType.APPLICATION_JSON));\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/user/{id}\", 1).header(name, values));\n\t\t\n//\t\tmockMvc.perform(MockMvcRequestBuilders.get(\"/index\"))\n//\t\t.andDo(MockMvcResultHandlers.print());\n\t\t\n\n\t}\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.example</groupId>\n\t<artifactId>Spring-Boot-Swagger2</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.11.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>io.springfox</groupId>\n\t\t    <artifactId>springfox-swagger2</artifactId>\n\t\t    <version>2.6.1</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>io.springfox</groupId>\n\t\t    <artifactId>springfox-swagger-ui</artifactId>\n\t\t    <version>2.6.1</version>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t\tSystem.out.println(\"complete\");\n\t}\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/java/com/example/demo/config/SwaggerConfig.java",
    "content": "package com.example.demo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@Configuration\n@EnableSwagger2\npublic class SwaggerConfig {\n\t\n\t@Bean\n    public Docket buildDocket() {\n        return new Docket(DocumentationType.SWAGGER_2)\n                .apiInfo(buildApiInf())\n                .select()\n                .apis(RequestHandlerSelectors.basePackage(\"com.example.demo.controller\"))\n                .paths(PathSelectors.any())\n                .build();\n    }\n    private ApiInfo buildApiInf() {\n        return new ApiInfoBuilder()\n                .title(\"系统RESTful API文档\")\n                .contact(new Contact(\"mrbird\", \"https://mrbird.cc\", \"852252810@qq.com\"))\n                .version(\"1.0\")\n                .build();\n    }\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/java/com/example/demo/config/WebConfig.java",
    "content": "package com.example.demo.config;\n\nimport java.text.SimpleDateFormat;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\n\n@Configuration\npublic class WebConfig {\n\t@Bean\n\tpublic ObjectMapper getObjectMapper() {\n\t\tObjectMapper mapper = new ObjectMapper();\n\t\tmapper.setDateFormat(new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\"));\n\t\treturn mapper;\n\t}\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.DeleteMapping;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.PutMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport com.example.demo.domain.User;\n\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiImplicitParams;\nimport io.swagger.annotations.ApiOperation;\nimport springfox.documentation.annotations.ApiIgnore;\n\n@Api(value = \"用户Controller\")\n@Controller\n@RequestMapping(\"user\")\npublic class UserController {\n\n\t@ApiIgnore\n\t@GetMapping(\"hello\")\n\tpublic @ResponseBody String hello() {\n\t\treturn \"hello\";\n\t}\n\n\t@ApiOperation(value = \"获取用户信息\", notes = \"根据用户id获取用户信息\")\n\t@ApiImplicitParam(name = \"id\", value = \"用户id\", required = true, dataType = \"Long\", paramType = \"path\")\n\t@GetMapping(\"/{id}\")\n\tpublic @ResponseBody User getUserById(@PathVariable(value = \"id\") Long id) {\n\t\tUser user = new User();\n\t\tuser.setId(id);\n\t\tuser.setName(\"mrbird\");\n\t\tuser.setAge(25);\n\t\treturn user;\n\t}\n\n\t@ApiOperation(value = \"获取用户列表\", notes = \"获取用户列表\")\n\t@GetMapping(\"/list\")\n\tpublic @ResponseBody List<User> getUserList() {\n\t\tList<User> list = new ArrayList<>();\n\t\tUser user1 = new User();\n\t\tuser1.setId(1l);\n\t\tuser1.setName(\"mrbird\");\n\t\tuser1.setAge(25);\n\t\tlist.add(user1);\n\t\tUser user2 = new User();\n\t\tuser2.setId(2l);\n\t\tuser2.setName(\"scott\");\n\t\tuser2.setAge(29);\n\t\tlist.add(user2);\n\t\treturn list;\n\t}\n\n\t@ApiOperation(value = \"新增用户\", notes = \"根据用户实体创建用户\")\n\t@ApiImplicitParam(name = \"user\", value = \"用户实体\", required = true, dataType = \"User\")\n\t@PostMapping(\"/add\")\n\tpublic @ResponseBody Map<String, Object> addUser(@RequestBody User user) {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"result\", \"success\");\n\t\treturn map;\n\t}\n\n\t@ApiOperation(value = \"删除用户\", notes = \"根据用户id删除用户\")\n\t@ApiImplicitParam(name = \"id\", value = \"用户id\", required = true, dataType = \"Long\", paramType = \"path\")\n\t@DeleteMapping(\"/{id}\")\n\tpublic @ResponseBody Map<String, Object> deleteUser(@PathVariable(value = \"id\") Long id) {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"result\", \"success\");\n\t\treturn map;\n\t}\n\n\t@ApiOperation(value = \"更新用户\", notes = \"根据用户id更新用户\")\n\t@ApiImplicitParams({\n\t\t\t@ApiImplicitParam(name = \"id\", value = \"用户id\", required = true, dataType = \"Long\", paramType = \"path\"),\n\t\t\t@ApiImplicitParam(name = \"user\", value = \"用户实体\", required = true, dataType = \"User\") })\n\t@PutMapping(\"/{id}\")\n\tpublic @ResponseBody Map<String, Object> updateUser(@PathVariable(value = \"id\") Long id, @RequestBody User user) {\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"result\", \"success\");\n\t\treturn map;\n\t}\n\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n\tprivate static final long serialVersionUID = -2731598327208972274L;\n\n\tprivate Long id;\n\tprivate String name;\n\tprivate Integer age;\n\n\tpublic Long getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(Long id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic Integer getAge() {\n\t\treturn age;\n\t}\n\n\tpublic void setAge(Integer age) {\n\t\tthis.age = age;\n\t}\n\n}\n"
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/main/resources/application.yml",
    "content": ""
  },
  {
    "path": "20.Spring-Boot-Swagger2/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "21.Spring-Boot-Actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Actuator</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Actuator</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-actuator</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "21.Spring-Boot-Actuator/src/main/java/com/springboot/demo/DemoApplication.java",
    "content": "package com.springboot.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@SpringBootApplication\npublic class DemoApplication {\n\n\t@RequestMapping(\"/\")\n\tString index() {\n\t\treturn \"hello spring boot\";\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "21.Spring-Boot-Actuator/src/main/resources/application.yml",
    "content": "server:\n  port: 80\n\nmanagement:\n  security:\n    enabled: false #关掉安全认证\n  port: 80\n  context-path: /monitor #actuator的访问路径\nendpoints:\n  shutdown:\n    enabled: true\n  beans:\n    id: instances"
  },
  {
    "path": "21.Spring-Boot-Actuator/src/test/java/com/springboot/demo/DemoApplicationTests.java",
    "content": "package com.springboot.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "22.Spring-Boot-Email/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Email</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Email</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t\t<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>\n    \t<thymeleaf-layout-dialect.version>2.0.1</thymeleaf-layout-dialect.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-mail</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t    <groupId>org.springframework.boot</groupId>\n\t\t    <artifactId>spring-boot-starter-thymeleaf</artifactId>\n\t\t</dependency>\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "22.Spring-Boot-Email/src/main/java/com/springboot/demo/DemoApplication.java",
    "content": "package com.springboot.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "22.Spring-Boot-Email/src/main/java/com/springboot/demo/controller/EmailController.java",
    "content": "package com.springboot.demo.controller;\n\nimport java.io.File;\n\nimport javax.mail.internet.MimeMessage;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\nimport org.springframework.mail.javamail.MimeMessageHelper;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.thymeleaf.TemplateEngine;\nimport org.thymeleaf.context.Context;\n\n@RestController\n@RequestMapping(\"/email\")\npublic class EmailController {\n\n\t@Autowired\n\tprivate JavaMailSender jms;\n\n\t@Value(\"${spring.mail.username}\")\n\tprivate String from;\n\t\n    @Autowired\n    private TemplateEngine templateEngine;\n\n\t@RequestMapping(\"sendSimpleEmail\")\n\tpublic String sendSimpleEmail() {\n\t\ttry {\n\t\t\tSimpleMailMessage message = new SimpleMailMessage();\n\t\t\tmessage.setFrom(from);\n\t\t\tmessage.setTo(\"888888@qq.com\"); // 接收地址\n\t\t\tmessage.setSubject(\"一封简单的邮件\"); // 标题\n\t\t\tmessage.setText(\"使用Spring Boot发送简单邮件。\"); // 内容\n\t\t\tjms.send(message);\n\t\t\treturn \"发送成功\";\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn e.getMessage();\n\t\t}\n\t}\n\n\t@RequestMapping(\"sendHtmlEmail\")\n\tpublic String sendHtmlEmail() {\n\t\tMimeMessage message = null;\n\t\ttry {\n\t\t\tmessage = jms.createMimeMessage();\n\t\t\tMimeMessageHelper helper = new MimeMessageHelper(message, true);\n\t\t\thelper.setFrom(from); \n\t\t\thelper.setTo(\"888888@qq.com\"); // 接收地址\n\t\t\thelper.setSubject(\"一封HTML格式的邮件\"); // 标题\n\t\t\t// 带HTML格式的内容\n\t\t\tStringBuffer sb = new StringBuffer(\"<p style='color:#42b983'>使用Spring Boot发送HTML格式邮件。</p>\");\n\t\t\thelper.setText(sb.toString(), true);\n\t\t\tjms.send(message);\n\t\t\treturn \"发送成功\";\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn e.getMessage();\n\t\t}\n\t}\n\t\n\t@RequestMapping(\"sendAttachmentsMail\")\n\tpublic String sendAttachmentsMail() {\n\t\tMimeMessage message = null;\n\t\ttry {\n\t\t\tmessage = jms.createMimeMessage();\n\t\t\tMimeMessageHelper helper = new MimeMessageHelper(message, true);\n\t\t\thelper.setFrom(from); \n\t\t\thelper.setTo(\"888888@qq.com\"); // 接收地址\n\t\t\thelper.setSubject(\"一封带附件的邮件\"); // 标题\n\t\t\thelper.setText(\"详情参见附件内容！\"); // 内容\n\t\t\t// 传入附件\n\t\t\tFileSystemResource file = new FileSystemResource(new File(\"src/main/resources/static/file/项目文档.docx\"));\n            helper.addAttachment(\"项目文档.docx\", file);\n            jms.send(message);\n\t\t\treturn \"发送成功\";\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn e.getMessage();\n\t\t}\n\t}\n\t\n\t@RequestMapping(\"sendInlineMail\")\n\tpublic String sendInlineMail() {\n\t\tMimeMessage message = null;\n\t\ttry {\n\t\t\tmessage = jms.createMimeMessage();\n\t\t\tMimeMessageHelper helper = new MimeMessageHelper(message, true);\n\t\t\thelper.setFrom(from); \n\t\t\thelper.setTo(\"888888@qq.com\"); // 接收地址\n\t\t\thelper.setSubject(\"一封带静态资源的邮件\"); // 标题\n\t\t\thelper.setText(\"<html><body>博客图：<img src='cid:img'/></body></html>\", true); // 内容\n\t\t\t// 传入附件\n\t\t\tFileSystemResource file = new FileSystemResource(new File(\"src/main/resources/static/img/sunshine.png\"));\n            helper.addInline(\"img\", file); \n            jms.send(message);\n\t\t\treturn \"发送成功\";\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn e.getMessage();\n\t\t}\n\t}\n\t\n\t@RequestMapping(\"sendTemplateEmail\")\n\tpublic String sendTemplateEmail(String code) {\n\t\tMimeMessage message = null;\n\t\ttry {\n\t\t\tmessage = jms.createMimeMessage();\n\t\t\tMimeMessageHelper helper = new MimeMessageHelper(message, true);\n\t\t\thelper.setFrom(from); \n\t\t\thelper.setTo(\"888888@qq.com\"); // 接收地址\n\t\t\thelper.setSubject(\"邮件摸板测试\"); // 标题\n\t\t\t// 处理邮件模板\n\t\t    Context context = new Context();\n\t\t    context.setVariable(\"code\", code);\n\t\t    String template = templateEngine.process(\"emailTemplate\", context);\n\t\t\thelper.setText(template, true);\n\t\t\tjms.send(message);\n\t\t\treturn \"发送成功\";\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\treturn e.getMessage();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "22.Spring-Boot-Email/src/main/resources/application.yml",
    "content": "server:\n  port: 80\n\nspring:\n  mail:\n    host: smtp.163.com\n    username: xxxx@163.com\n    password: xxxx\n    properties:\n      mail:\n        smtp:\n          auth: true\n          starttls:\n            enable: true\n            required: true"
  },
  {
    "path": "22.Spring-Boot-Email/src/main/resources/templates/emailTemplate.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh\" xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <meta charset=\"UTF-8\" />\n    <title>模板</title>\n</head>\n<body>\n    您好，您的验证码为<span th:text=\"${code}\"></span>，请在两分钟内使用完成操作。\n</body>\n</html>"
  },
  {
    "path": "22.Spring-Boot-Email/src/test/java/com/springboot/demo/DemoApplicationTests.java",
    "content": "package com.springboot.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.example</groupId>\n\t<artifactId>Spring-Boot-admin-Client</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>demo</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-client -->\n\t\t<dependency>\n\t\t\t<groupId>de.codecentric</groupId>\n\t\t\t<artifactId>spring-boot-admin-starter-client</artifactId>\n\t\t\t<version>1.5.7</version>\n\t\t</dependency>\n\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Client/src/main/resources/application.yml",
    "content": "management:\n  security:\n    enabled: false\n    \nserver:\n  port: 8081\n  \nspring:\n  boot:\n    admin:\n      url: http://localhost:8080/admin-server\n      \ninfo: \n  app:  \n    name: \"@project.name@\"\n    description: \"@project.description@\"  \n    version: \"@project.version@\"  \n    spring-boot-version: \"@project.parent.version@\" \n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Client/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.example</groupId>\n\t<artifactId>Spring-Boot-Admin-Server</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Sprign Boot Admin Server</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-server -->\n\t\t<dependency>\n\t\t\t<groupId>de.codecentric</groupId>\n\t\t\t<artifactId>spring-boot-admin-server</artifactId>\n\t\t\t<version>1.5.7</version>\n\t\t</dependency>\n\n\t\t<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-server-ui -->\n\t\t<dependency>\n\t\t\t<groupId>de.codecentric</groupId>\n\t\t\t<artifactId>spring-boot-admin-server-ui</artifactId>\n\t\t\t<version>1.5.7</version>\n\t\t</dependency>\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-mail</artifactId>\n</dependency>\n\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Server/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport de.codecentric.boot.admin.config.EnableAdminServer;\n\n@SpringBootApplication\n@EnableAutoConfiguration\n@EnableAdminServer\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Server/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\n  context-path: /admin-server\n  \nspring:\n  mail:\n    host: smtp.163.com\n    username: xxx@163.com\n    password: xxx\n    properties:\n      mail:\n        smtp:\n          auth: true\n          starttls:\n            enable: true\n            required: true\n            \n  boot:\n    admin:\n      notify:\n        mail:\n          from: xxx@163.com\n          to: xxx@qq.com"
  },
  {
    "path": "23.Spring-Boot-Admin/Spring Boot Admin Server/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "24.Spring-Boot-Devtools/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Devtools</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Devtools</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath /> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-devtools</artifactId>\n\t\t\t\t<optional>true</optional>\n\t\t\t</dependency>\n\t</dependencies>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>nexus-aliyun</id>\n\t\t\t<name>Nexus aliyun</name>\n\t\t\t<url>http://maven.aliyun.com/nexus/content/groups/public</url>\n\t\t</repository>\n\t</repositories>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<fork>true</fork>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "24.Spring-Boot-Devtools/src/main/java/com/springboot/demo/DemoApplication.java",
    "content": "package com.springboot.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@SpringBootApplication\npublic class DemoApplication {\n\n\t@RequestMapping(\"/\")\n\tString index() {\n\t\treturn \"hello world\";\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "24.Spring-Boot-Devtools/src/main/resources/application.yml",
    "content": ""
  },
  {
    "path": "24.Spring-Boot-Devtools/src/test/java/com/springboot/demo/DemoApplicationTests.java",
    "content": "package com.springboot.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "25.Spring-Boot-Exception/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Exception</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Exception</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.14.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/java/cc/mrbird/Application.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/java/cc/mrbird/controller/UserController.java",
    "content": "package cc.mrbird.controller;\n\nimport cc.mrbird.exception.UserNotExistException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public void get(@PathVariable String id) {\n        throw new UserNotExistException(id);\n    }\n}\n"
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/java/cc/mrbird/exception/UserNotExistException.java",
    "content": "package cc.mrbird.exception;\n\npublic class UserNotExistException extends RuntimeException{\n    private static final long serialVersionUID = -1574716826948451793L;\n\n    private String id;\n\n    public UserNotExistException(String id){\n        super(\"user not exist\");\n        this.id = id;\n    }\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n}\n\n"
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/java/cc/mrbird/handler/ControllerExceptionHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport cc.mrbird.exception.UserNotExistException;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.ResponseStatus;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@ControllerAdvice\npublic class ControllerExceptionHandler {\n\n    @ExceptionHandler(UserNotExistException.class)\n    @ResponseBody\n    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n    public Map<String, Object> handleUserNotExistsException(UserNotExistException e) {\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"id\", e.getId());\n        map.put(\"message\", e.getMessage());\n        return map;\n    }\n}\n"
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "25.Spring-Boot-Exception/src/main/resources/resources/error/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>500</title>\n</head>\n<body>\n    系统内部异常\n</body>\n</html>"
  },
  {
    "path": "25.Spring-Boot-Exception/src/test/java/cc/mrbird/cc/mrbird/demo/ApplicationTests.java",
    "content": "package cc.mrbird.cc.mrbird.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class ApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Filter-Interceptor</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Filter-Interceptor</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.14.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/java/cc/mrbird/Application.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/java/cc/mrbird/config/WebConfig.java",
    "content": "package cc.mrbird.config;\n\nimport cc.mrbird.filter.TimeFilter;\nimport cc.mrbird.interceptor.TimeInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Configuration\npublic class WebConfig extends WebMvcConfigurerAdapter {\n    @Bean\n    public FilterRegistrationBean timeFilter() {\n        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();\n        TimeFilter timeFilter = new TimeFilter();\n        filterRegistrationBean.setFilter(timeFilter);\n\n        List<String> urlList = new ArrayList<>();\n        urlList.add(\"/*\");\n\n        filterRegistrationBean.setUrlPatterns(urlList);\n        return filterRegistrationBean;\n    }\n\n    @Autowired\n    private TimeInterceptor timeInterceptor;\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(timeInterceptor);\n    }\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/java/cc/mrbird/controller/UserController.java",
    "content": "package cc.mrbird.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public void get(@PathVariable String id) {\n        System.out.println(id);\n        // throw new RuntimeException(\"user not exist\");\n    }\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/java/cc/mrbird/filter/TimeFilter.java",
    "content": "package cc.mrbird.filter;\n\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\nimport java.util.Date;\n\n// @Component\n// @WebFilter(urlPatterns = {\"/*\"})\npublic class TimeFilter implements Filter {\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        System.out.println(\"过滤器初始化\");\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        System.out.println(\"开始执行过滤器\");\n        Long start = new Date().getTime();\n        filterChain.doFilter(servletRequest, servletResponse);\n        System.out.println(\"【过滤器】耗时 \" + (new Date().getTime() - start));\n        System.out.println(\"结束执行过滤器\");\n    }\n\n    @Override\n    public void destroy() {\n        System.out.println(\"过滤器销毁\");\n    }\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/java/cc/mrbird/interceptor/TimeInterceptor.java",
    "content": "package cc.mrbird.interceptor;\n\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Date;\n\n@Component\npublic class TimeInterceptor implements HandlerInterceptor {\n    @Override\n    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {\n        System.out.println(\"处理拦截之前\");\n        httpServletRequest.setAttribute(\"startTime\", new Date().getTime());\n        System.out.println(((HandlerMethod) o).getBean().getClass().getName());\n        System.out.println(((HandlerMethod) o).getMethod().getName());\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {\n        System.out.println(\"开始处理拦截\");\n        Long start = (Long) httpServletRequest.getAttribute(\"startTime\");\n        System.out.println(\"【拦截器】耗时 \" + (new Date().getTime() - start));\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {\n        System.out.println(\"处理拦截之后\");\n        Long start = (Long) httpServletRequest.getAttribute(\"startTime\");\n        System.out.println(\"【拦截器】耗时 \" + (new Date().getTime() - start));\n        System.out.println(\"异常信息 \" + e);\n    }\n}\n"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/main/resources/resources/error/500.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>500</title>\n</head>\n<body>\n    系统内部异常\n</body>\n</html>"
  },
  {
    "path": "26.Spring-Boot-Filter-Interceptor/src/test/java/cc/mrbird/cc/mrbird/demo/ApplicationTests.java",
    "content": "package cc.mrbird.cc.mrbird.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class ApplicationTests {\n\n\t@Test\n\tpublic void contextLoads() {\n\t}\n\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>com.springboot</groupId>\n\t<artifactId>Spring-Boot-Mapper-PageHelper</artifactId>\n\t<version>0.0.1-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>Spring-Boot-Mapper-PageHelper</name>\n\t<description>Demo project for Spring Boot</description>\n\n\t<parent>\n\t\t<groupId>org.springframework.boot</groupId>\n\t\t<artifactId>spring-boot-starter-parent</artifactId>\n\t\t<version>1.5.9.RELEASE</version>\n\t\t<relativePath/> <!-- lookup parent from repository -->\n\t</parent>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\t\t<java.version>1.7</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-web</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t<artifactId>spring-boot-starter-test</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!--mybatis-->\n\t\t<dependency>\n\t\t\t<groupId>org.mybatis.spring.boot</groupId>\n\t\t\t<artifactId>mybatis-spring-boot-starter</artifactId>\n\t\t\t<version>1.3.1</version>\n\t\t</dependency>\n\t\t<!--通用mapper-->\n\t\t<dependency>\n\t\t\t<groupId>tk.mybatis</groupId>\n\t\t\t<artifactId>mapper-spring-boot-starter</artifactId>\n\t\t\t<version>1.1.5</version>\n\t\t</dependency>\n\t\t<!--pagehelper 分页插件-->\n\t\t<dependency>\n\t\t\t<groupId>com.github.pagehelper</groupId>\n\t\t\t<artifactId>pagehelper-spring-boot-starter</artifactId>\n\t\t\t<version>1.2.3</version>\n\t\t</dependency>\n\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t<version>6.0</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t   <groupId>com.alibaba</groupId>\n\t\t   <artifactId>druid-spring-boot-starter</artifactId>\n\t\t   <version>1.1.6</version>\n\t\t</dependency>\n\t\t\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.springframework.boot</groupId>\n\t\t\t\t<artifactId>spring-boot-maven-plugin</artifactId>\n\t\t\t</plugin>\n\t\t\t\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.mybatis.generator</groupId>\n\t\t\t\t<artifactId>mybatis-generator-maven-plugin</artifactId>\n\t\t\t\t<version>1.3.5</version>\n\t\t\t\t<dependencies>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>com.oracle</groupId>\n\t\t\t\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t\t\t\t<version>6.0</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t\t<dependency>\n\t\t\t\t\t\t<groupId>tk.mybatis</groupId>\n\t\t\t\t\t\t<artifactId>mapper</artifactId>\n\t\t\t\t\t\t<version>3.4.0</version>\n\t\t\t\t\t</dependency>\n\t\t\t\t</dependencies>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>Generate MyBatis Artifacts</id>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>generate</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<!--允许移动生成的文件 -->\n\t\t\t\t\t<verbose>true</verbose>\n\t\t\t\t\t<!-- 是否覆盖 -->\n\t\t\t\t\t<overwrite>true</overwrite>\n\t\t\t\t\t<!-- 自动生成的配置 -->\n\t\t\t\t\t<configurationFile>src/main/resources/mybatis-generator.xml</configurationFile>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\n</project>\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/Application.java",
    "content": "package com.springboot;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n\n@SpringBootApplication\n@MapperScan(\"com.springboot.mapper\")\npublic class Application {\n\t\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(Application.class,args);\n\t}\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/ApplicationTest.java",
    "content": "package com.springboot;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringJUnit4ClassRunner;\n\nimport com.github.pagehelper.PageHelper;\nimport com.github.pagehelper.PageInfo;\nimport com.springboot.bean.User;\nimport com.springboot.service.UserService;\n\nimport tk.mybatis.mapper.entity.Example;\n\n@RunWith(SpringJUnit4ClassRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ApplicationTest {\n\n\t@Autowired\n\tprivate UserService userService;\n\n\t@Test\n\tpublic void test() throws Exception {\n\n\t\t// User user = new User();\n\t\t// user.setId(userService.getSequence(\"seq_user\"));\n\t\t// user.setUsername(\"scott\");\n\t\t// user.setPasswd(\"ac089b11709f9b9e9980e7c497268dfa\");\n\t\t// user.setCreateTime(new Date());\n\t\t// user.setStatus(\"0\");\n\t\t// this.userService.save(user);\n\n\t\t\n//\t\tExample example = new Example(User.class);\n//\t\texample.createCriteria().andCondition(\"username like '%i%'\");\n//\t\texample.setOrderByClause(\"id desc\");\n//\t\tList<User> userList = this.userService.selectByExample(example);\n//\t\tfor (User u : userList) {\n//\t\t\tSystem.out.println(u.getUsername());\n//\t\t}\n//\t\t\n//\t\tList<User> all = this.userService.selectAll();\n//\t\tfor (User u : all) {\n//\t\t\tSystem.out.println(u.getUsername());\n//\t\t}\n//\t\t\n//\t\tUser user = new User();\n//\t\tuser.setId(1l);\n//\t\tuser = this.userService.selectByKey(user);\n//\t\tSystem.out.println(user.getUsername());\n//\n//\t\tuser.setId(4l);\n//\t\tthis.userService.delete(user);\n\n\t\tPageHelper.startPage(2, 2);\n\t\tList<User> list = userService.selectAll();\n\t\tPageInfo<User> pageInfo = new PageInfo<User>(list);\n\t\tList<User> result = pageInfo.getList();\n\t\tfor (User u : result) {\n\t\t\tSystem.out.println(u.getUsername());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/bean/User.java",
    "content": "package com.springboot.bean;\n\nimport java.util.Date;\n\nimport javax.persistence.Column;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\n\n@Table(name = \"T_USER\")\npublic class User {\n    @Id\n    @Column(name = \"ID\")\n    private Long id;\n\n    @Column(name = \"USERNAME\")\n    private String username;\n\n    @Column(name = \"PASSWD\")\n    private String passwd;\n\n    @Column(name = \"CREATE_TIME\")\n    private Date createTime;\n\n    @Column(name = \"STATUS\")\n    private String status;\n\n    /**\n     * @return ID\n     */\n    public Long getId() {\n        return id;\n    }\n\n    /**\n     * @param id\n     */\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    /**\n     * @return USERNAME\n     */\n    public String getUsername() {\n        return username;\n    }\n\n    /**\n     * @param username\n     */\n    public void setUsername(String username) {\n        this.username = username == null ? null : username.trim();\n    }\n\n    /**\n     * @return PASSWD\n     */\n    public String getPasswd() {\n        return passwd;\n    }\n\n    /**\n     * @param passwd\n     */\n    public void setPasswd(String passwd) {\n        this.passwd = passwd == null ? null : passwd.trim();\n    }\n\n    /**\n     * @return CREATE_TIME\n     */\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    /**\n     * @param createTime\n     */\n    public void setCreateTime(Date createTime) {\n        this.createTime = createTime;\n    }\n\n    /**\n     * @return STATUS\n     */\n    public String getStatus() {\n        return status;\n    }\n\n    /**\n     * @param status\n     */\n    public void setStatus(String status) {\n        this.status = status == null ? null : status.trim();\n    }\n}"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/config/MyMapper.java",
    "content": "package com.springboot.config;\n\nimport tk.mybatis.mapper.common.Mapper;\nimport tk.mybatis.mapper.common.MySqlMapper;\n\npublic interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {\n\t\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/mapper/SeqenceMapper.java",
    "content": "package com.springboot.mapper;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\npublic interface SeqenceMapper {\n\t@Select(\"select ${seqName}.nextval from dual\")\n\tLong getSequence(@Param(\"seqName\") String seqName);\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/mapper/UserMapper.java",
    "content": "package com.springboot.mapper;\n\nimport com.springboot.bean.User;\nimport com.springboot.config.MyMapper;\n\npublic interface UserMapper extends MyMapper<User> {\n}"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/service/IService.java",
    "content": "package com.springboot.service;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic interface IService<T> {\n\n\tLong getSequence(@Param(\"seqName\") String seqName);\n\t\n\tList<T> selectAll();\n\t\n    T selectByKey(Object key);\n\n    int save(T entity);\n\n    int delete(Object key);\n\n    int updateAll(T entity);\n\n    int updateNotNull(T entity);\n\n    List<T> selectByExample(Object example);\n\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/service/UserService.java",
    "content": "package com.springboot.service;\n\nimport com.springboot.bean.User;\n\npublic interface UserService extends IService<User>{\n\t\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/service/impl/BaseService.java",
    "content": "package com.springboot.service.impl;\n\nimport java.util.List;\n\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.springboot.mapper.SeqenceMapper;\nimport com.springboot.service.IService;\n\nimport tk.mybatis.mapper.common.Mapper;\n\npublic abstract class BaseService<T> implements IService<T> {\n\n    @Autowired\n    protected Mapper<T> mapper;\n    @Autowired\n    protected SeqenceMapper seqenceMapper;\n    \n    public Mapper<T> getMapper() {\n        return mapper;\n    }\n    @Override\n    public Long getSequence(@Param(\"seqName\") String seqName){\n    \treturn seqenceMapper.getSequence(seqName);\n    }\n    \n    @Override\n    public List<T> selectAll() {\n        //说明：查询所有数据\n        return mapper.selectAll();\n    }\n    \n    @Override\n    public T selectByKey(Object key) {\n        //说明：根据主键字段进行查询，方法参数必须包含完整的主键属性，查询条件使用等号\n        return mapper.selectByPrimaryKey(key);\n    }\n\n    @Override\n    public int save(T entity) {\n        //说明：保存一个实体，null的属性也会保存，不会使用数据库默认值\n        return mapper.insert(entity);\n    }\n\n    @Override\n    public int delete(Object key) {\n        //说明：根据主键字段进行删除，方法参数必须包含完整的主键属性\n        return mapper.deleteByPrimaryKey(key);\n    }\n\n    @Override\n    public int updateAll(T entity) {\n        //说明：根据主键更新实体全部字段，null值会被更新\n        return mapper.updateByPrimaryKey(entity);\n    }\n\n    @Override\n    public int updateNotNull(T entity) {\n        //根据主键更新属性不为null的值\n        return mapper.updateByPrimaryKeySelective(entity);\n    }\n\n    @Override\n    public List<T> selectByExample(Object example) {\n        //说明：根据Example条件进行查询\n        //重点：这个查询支持通过Example类指定查询列，通过selectProperties方法指定查询列\n        return mapper.selectByExample(example);\n    }\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/java/com/springboot/service/impl/UserServiceImpl.java",
    "content": "package com.springboot.service.impl;\n\nimport org.springframework.stereotype.Repository;\n\nimport com.springboot.bean.User;\nimport com.springboot.service.UserService;\n\n@Repository(\"userService\")\npublic class UserServiceImpl extends BaseService<User> implements UserService{\n\n\t\n}\n"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/resources/application.yml",
    "content": "server:\n  context-path: /web\n\nspring:\n  datasource:\n    druid:\n      # 数据库访问配置, 使用druid数据源\n      type: com.alibaba.druid.pool.DruidDataSource\n      driver-class-name: oracle.jdbc.driver.OracleDriver\n      url: jdbc:oracle:thin:@localhost:1521:ORCL\n      username: scott\n      password: 6742530\n      # 连接池配置\n      initial-size: 5\n      min-idle: 5\n      max-active: 20\n      # 连接等待超时时间\n      max-wait: 30000\n      # 配置检测可以关闭的空闲连接间隔时间\n      time-between-eviction-runs-millis: 60000\n      # 配置连接在池中的最小生存时间\n      min-evictable-idle-time-millis: 300000\n      validation-query: select '1' from dual\n      test-while-idle: true\n      test-on-borrow: false\n      test-on-return: false\n      # 打开PSCache，并且指定每个连接上PSCache的大小\n      pool-prepared-statements: true\n      max-open-prepared-statements: 20\n      max-pool-prepared-statement-per-connection-size: 20\n      # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙\n      filters: stat,wall\n      # Spring监控AOP切入点，如x.y.z.service.*,配置多个英文逗号分隔\n      aop-patterns: com.springboot.servie.*\n      \n    \n      # WebStatFilter配置\n      web-stat-filter:\n        enabled: true\n        # 添加过滤规则\n        url-pattern: /*\n        # 忽略过滤的格式\n        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'\n      \n      # StatViewServlet配置 \n      stat-view-servlet:\n        enabled: true\n        # 访问路径为/druid时，跳转到StatViewServlet\n        url-pattern: /druid/*\n        # 是否能够重置数据\n        reset-enable: false\n        # 需要账号密码才能访问控制台\n        login-username: druid\n        login-password: druid123\n        # IP白名单\n        # allow: 127.0.0.1\n        #　IP黑名单（共同存在时，deny优先于allow）\n        # deny: 192.168.1.218\n      \n      # 配置StatFilter\n      filter: \n        stat: \n          log-slow-sql: true\n\nmybatis:\n  # type-aliases扫描路径\n  type-aliases-package: com.springboot.bean\n  # mapper xml实现扫描路径\n  mapper-locations: classpath:mapper/*.xml\n  property:\n    order: BEFORE\n\n\n#mappers 多个接口时逗号隔开\nmapper:\n  mappers: com.springboot.config.MyMapper\n  not-empty: false\n  identity: oracle\n\n#pagehelper\npagehelper: \n  helperDialect: oracle\n  reasonable: true\n  supportMethodsArguments: true\n  params: count=countSql\n\n    \nlogging:\n  level:\n    com:\n      springboot:\n        mapper: debug"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.springboot.mapper.UserMapper\">\n  <resultMap id=\"BaseResultMap\" type=\"com.springboot.bean.User\">\n    <!--\n      WARNING - @mbg.generated\n    -->\n    <id column=\"ID\" jdbcType=\"DECIMAL\" property=\"id\" />\n    <result column=\"USERNAME\" jdbcType=\"VARCHAR\" property=\"username\" />\n    <result column=\"PASSWD\" jdbcType=\"VARCHAR\" property=\"passwd\" />\n    <result column=\"CREATE_TIME\" jdbcType=\"TIMESTAMP\" property=\"createTime\" />\n    <result column=\"STATUS\" jdbcType=\"CHAR\" property=\"status\" />\n  </resultMap>\n</mapper>"
  },
  {
    "path": "27.Spring-Boot-Mapper-PageHelper/src/main/resources/mybatis-generator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE generatorConfiguration\n        PUBLIC \"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd\">\n<generatorConfiguration>\n\n    <context id=\"oracle\" targetRuntime=\"MyBatis3Simple\" defaultModelType=\"flat\">\n\n        <plugin type=\"tk.mybatis.mapper.generator.MapperPlugin\">\n            <property name=\"mappers\" value=\"com.springboot.config.MyMapper\" />\n            <!--caseSensitive默认false，当数据库表名区分大小写时，可以将该属性设置为true-->\n          <property name=\"caseSensitive\" value=\"false\"/>\n        </plugin>\n\n        <!-- 阻止生成自动注释 -->\n        <commentGenerator>\n            <property name=\"javaFileEncoding\" value=\"UTF-8\"/>\n            <property name=\"suppressDate\" value=\"true\"/>\n            <property name=\"suppressAllComments\" value=\"true\"/>\n        </commentGenerator>\n\n        <!--数据库链接地址账号密码-->\n        <jdbcConnection driverClass=\"oracle.jdbc.driver.OracleDriver\"\n                        connectionURL=\"jdbc:oracle:thin:@localhost:1521:ORCL\"\n                        userId=\"scott\"\n                        password=\"6742530\">\n        </jdbcConnection>\n\n        <javaTypeResolver>\n            <property name=\"forceBigDecimals\" value=\"false\"/>\n        </javaTypeResolver>\n\n        <!--生成Model类存放位置-->\n        <javaModelGenerator targetPackage=\"com.springboot.bean\" targetProject=\"src/main/java\">\n            <property name=\"enableSubPackages\" value=\"true\"/>\n            <property name=\"trimStrings\" value=\"true\"/>\n        </javaModelGenerator>\n\n        <!--生成映射文件存放位置-->\n        <sqlMapGenerator targetPackage=\"mapper\" targetProject=\"src/main/resources\">\n            <property name=\"enableSubPackages\" value=\"true\"/>\n        </sqlMapGenerator>\n\n        <!--生成Dao类存放位置-->\n        <!-- 客户端代码，生成易于使用的针对Model对象和XML配置文件 的代码\n                type=\"ANNOTATEDMAPPER\",生成Java Model 和基于注解的Mapper对象\n                type=\"XMLMAPPER\",生成SQLMap XML文件和独立的Mapper接口 -->\n       <javaClientGenerator type=\"XMLMAPPER\" targetPackage=\"com.springboot.mapper\" targetProject=\"src/main/java\">\n            <property name=\"enableSubPackages\" value=\"true\"/>\n       </javaClientGenerator>\n\n        <!--生成对应表及类名去掉Mybatis Generator生成的一堆 example-->\n        <table tableName=\"T_user\" domainObjectName=\"User\" enableCountByExample=\"false\" enableUpdateByExample=\"false\" enableDeleteByExample=\"false\" enableSelectByExample=\"false\" selectByExampleQueryId=\"false\">\n            <generatedKey column=\"id\" sqlStatement=\"oralce\" identity=\"true\"/>\n        </table>\n    </context>\n</generatorConfiguration>"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Eureka-Service/src/main/resources/application.yml",
    "content": "spring:\n  profiles:\n    active: peer1\nsecurity:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Server-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Server-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Server-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Server-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Server-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/info\")\n    public String getInfo() {\n        return this.restTemplate.getForEntity(\"http://Server-Provider/info\", String.class).getBody();\n    }\n}\n"
  },
  {
    "path": "28.Spring-Cloud-Eureka-Server-Discovery/Server-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Server-Consumer\n    \neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Ribbon-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Ribbon-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Ribbon-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Ribbon-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Ribbon-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.util.UriComponentsBuilder;\n\nimport java.net.URI;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"user/{id:\\\\d+}\")\n    public User getUser(@PathVariable Long id) {\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"id\", id);\n        URI uri = UriComponentsBuilder.fromUriString(\"http://Server-Provider/user/{id}\")\n                .build().expand(params).encode().toUri();\n        return this.restTemplate.getForEntity(uri, User.class).getBody();\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return this.restTemplate.getForObject(\"http://Server-Provider/user\", List.class);\n    }\n\n    @GetMapping(\"user/add\")\n    public String addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        HttpStatus status = this.restTemplate.postForEntity(\"http://Server-Provider/user\", user, null).getStatusCode();\n        if (status.is2xxSuccessful()) {\n            return \"新增用户成功\";\n        } else {\n            return \"新增用户失败\";\n        }\n    }\n\n    @GetMapping(\"user/update\")\n    public void updateUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        this.restTemplate.put(\"http://Server-Provider/user\", user);\n    }\n\n    @GetMapping(\"user/delete/{id:\\\\d+}\")\n    public void deleteUser(@PathVariable Long id) {\n        this.restTemplate.delete(\"http://Server-Provider/user/{1}\", id);\n    }\n}\n"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Ribbon-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "29.Spring-Cloud-Ribbon-LoadBalance/Ribbon-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Server-Consumer\n    \neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping(\"users\")\n    public List<User> get(String ids) {\n        log.info(\"批量获取用户信息\");\n        List<User> list = new ArrayList<>();\n        for (String id : ids.split(\",\")) {\n            list.add(new User(Long.valueOf(id), \"user\" + id, \"123456\"));\n        }\n        return list;\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Ribbon-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Ribbon-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-hystrix -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringCloudApplication\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/java/com/example/demo/Service/UserService.java",
    "content": "package com.example.demo.Service;\n\nimport com.example.demo.domain.User;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport com.netflix.hystrix.contrib.javanica.command.AsyncResult;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n/**\n * @author MrBird\n */\n@Service(\"userService\")\npublic class UserService {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(batchMethod = \"findUserBatch\", collapserProperties = {\n            @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"100\")\n    })\n    public Future<User> findUser(Long id) {\n        log.info(\"获取单个用户信息\");\n        // return new AsyncResult<User>() {\n        //     @Override\n        //     public User invoke() {\n        //         return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n        //     }\n        // };\n        return null;\n    }\n\n    @HystrixCommand\n    public List<User> findUserBatch(List<Long> ids) {\n        log.info(\"批量获取用户信息,ids: \" + ids);\n        User[] users = restTemplate.getForObject(\"http://Server-Provider/user/users?ids={1}\", User[].class, StringUtils.join(ids, \",\"));\n        return Arrays.asList(users);\n    }\n\n    public String getCacheKey(Long id) {\n        return String.valueOf(id);\n    }\n\n    @CacheResult(cacheKeyMethod = \"getCacheKey\")\n    @HystrixCommand(fallbackMethod = \"getUserDefault\", commandKey = \"getUserById\", groupKey = \"userGroup\",\n            threadPoolKey = \"getUserThread\")\n    public User getUser(Long id) {\n        log.info(\"获取用户信息\");\n        return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n    }\n\n    @HystrixCommand(fallbackMethod = \"getUserDefault2\")\n    public User getUserDefault(Long id) {\n        String a = null;\n        // 测试服务降级\n        a.toString();\n        User user = new User();\n        user.setId(-1L);\n        user.setUsername(\"defaultUser\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public User getUserDefault2(Long id, Throwable e) {\n        System.out.println(e.getMessage());\n        User user = new User();\n        user.setId(-2L);\n        user.setUsername(\"defaultUser2\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public List<User> getUsers() {\n        return this.restTemplate.getForObject(\"http://Server-Provider/user\", List.class);\n    }\n\n    public String addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        HttpStatus status = this.restTemplate.postForEntity(\"http://Server-Provider/user\", user, null).getStatusCode();\n        if (status.is2xxSuccessful()) {\n            return \"新增用户成功\";\n        } else {\n            return \"新增用户失败\";\n        }\n    }\n\n    @CacheRemove(commandKey = \"getUserById\")\n    @HystrixCommand\n    public void updateUser(@CacheKey(\"id\") User user) {\n        this.restTemplate.put(\"http://Server-Provider/user\", user);\n    }\n\n    public void deleteUser(@PathVariable Long id) {\n        this.restTemplate.delete(\"http://Server-Provider/user/{1}\", id);\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.Service.UserService;\nimport com.example.demo.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"testRequestMerge\")\n    public void testRequerstMerge() throws InterruptedException, ExecutionException {\n        Future<User> f1 = userService.findUser(1L);\n        Future<User> f2 = userService.findUser(2L);\n        Future<User> f3 = userService.findUser(3L);\n        f1.get();\n        f2.get();\n        f3.get();\n        Thread.sleep(200);\n        Future<User> f4 = userService.findUser(4L);\n        f4.get();\n    }\n\n    @GetMapping(\"testCache\")\n    public void testCache() {\n        userService.getUser(1L);\n        userService.getUser(1L);\n        userService.getUser(1L);\n    }\n\n    @GetMapping(\"user/{id}\")\n    public User getUser(@PathVariable Long id) {\n        return userService.getUser(id);\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return userService.getUsers();\n    }\n\n    @GetMapping(\"user/add\")\n    public String addUser() {\n        return userService.addUser();\n    }\n\n    @GetMapping(\"user/update\")\n    public void updateUser() {\n        userService.updateUser(new User(1L, \"mrbird\", \"123456\"));\n    }\n\n    @GetMapping(\"user/delete/{id:\\\\d+}\")\n    public void deleteUser(@PathVariable Long id) {\n        userService.deleteUser(id);\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/java/com/example/demo/filter/HystrixRequestContextServletFilter.java",
    "content": "package com.example.demo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\n@WebFilter(filterName = \"hystrixRequestContextServletFilter\", urlPatterns = \"/*\", asyncSupported = true)\npublic class HystrixRequestContextServletFilter implements Filter {\n    @Override\n    public void init(FilterConfig filterConfig) {\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        filterChain.doFilter(servletRequest, servletResponse);\n        context.close();\n    }\n\n    @Override\n    public void destroy() {\n    }\n}\n"
  },
  {
    "path": "30.Spring-Cloud-Hystrix-Circuit-Breaker/Ribbon-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Server-Consumer\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping(\"users\")\n    public List<User> get(String ids) {\n        log.info(\"批量获取用户信息\");\n        List<User> list = new ArrayList<>();\n        for (String id : ids.split(\",\")) {\n            list.add(new User(Long.valueOf(id), \"user\" + id, \"123456\"));\n        }\n        return list;\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Hystrix-Dashboard/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Hystrix-Dashboard</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Hystrix-Dashboard</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Hystrix-Dashboard/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;\n\n@SpringBootApplication\n@EnableHystrixDashboard\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Hystrix-Dashboard/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: Hystrix-Dashboard\nserver:\n  port: 9002\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Ribbon-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Ribbon-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-hystrix -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringCloudApplication\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/java/com/example/demo/Service/UserService.java",
    "content": "package com.example.demo.Service;\n\nimport com.example.demo.domain.User;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport com.netflix.hystrix.contrib.javanica.command.AsyncResult;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n/**\n * @author MrBird\n */\n@Service(\"userService\")\npublic class UserService {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(batchMethod = \"findUserBatch\", collapserProperties = {\n            @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"100\")\n    })\n    public Future<User> findUser(Long id) {\n        log.info(\"获取单个用户信息\");\n        // return new AsyncResult<User>() {\n        //     @Override\n        //     public User invoke() {\n        //         return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n        //     }\n        // };\n        return null;\n    }\n\n    @HystrixCommand\n    public List<User> findUserBatch(List<Long> ids) {\n        log.info(\"批量获取用户信息,ids: \" + ids);\n        User[] users = restTemplate.getForObject(\"http://Server-Provider/user/users?ids={1}\", User[].class, StringUtils.join(ids, \",\"));\n        return Arrays.asList(users);\n    }\n\n    public String getCacheKey(Long id) {\n        return String.valueOf(id);\n    }\n\n    @CacheResult(cacheKeyMethod = \"getCacheKey\")\n    @HystrixCommand(fallbackMethod = \"getUserDefault\", commandKey = \"getUserById\", groupKey = \"userGroup\",\n            threadPoolKey = \"getUserThread\")\n    public User getUser(Long id) {\n        log.info(\"获取用户信息\");\n        return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n    }\n\n    @HystrixCommand(fallbackMethod = \"getUserDefault2\")\n    public User getUserDefault(Long id) {\n        String a = null;\n        // 测试服务降级\n        a.toString();\n        User user = new User();\n        user.setId(-1L);\n        user.setUsername(\"defaultUser\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public User getUserDefault2(Long id, Throwable e) {\n        System.out.println(e.getMessage());\n        User user = new User();\n        user.setId(-2L);\n        user.setUsername(\"defaultUser2\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public List<User> getUsers() {\n        return this.restTemplate.getForObject(\"http://Server-Provider/user\", List.class);\n    }\n\n    public String addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        HttpStatus status = this.restTemplate.postForEntity(\"http://Server-Provider/user\", user, null).getStatusCode();\n        if (status.is2xxSuccessful()) {\n            return \"新增用户成功\";\n        } else {\n            return \"新增用户失败\";\n        }\n    }\n\n    @CacheRemove(commandKey = \"getUserById\")\n    @HystrixCommand\n    public void updateUser(@CacheKey(\"id\") User user) {\n        this.restTemplate.put(\"http://Server-Provider/user\", user);\n    }\n\n    public void deleteUser(@PathVariable Long id) {\n        this.restTemplate.delete(\"http://Server-Provider/user/{1}\", id);\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.Service.UserService;\nimport com.example.demo.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"testRequestMerge\")\n    public void testRequerstMerge() throws InterruptedException, ExecutionException {\n        Future<User> f1 = userService.findUser(1L);\n        Future<User> f2 = userService.findUser(2L);\n        Future<User> f3 = userService.findUser(3L);\n        f1.get();\n        f2.get();\n        f3.get();\n        Thread.sleep(200);\n        Future<User> f4 = userService.findUser(4L);\n        f4.get();\n    }\n\n    @GetMapping(\"testCache\")\n    public void testCache() {\n        userService.getUser(1L);\n        userService.getUser(1L);\n        userService.getUser(1L);\n    }\n\n    @GetMapping(\"user/{id}\")\n    public User getUser(@PathVariable Long id) {\n        return userService.getUser(id);\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return userService.getUsers();\n    }\n\n    @GetMapping(\"user/add\")\n    public String addUser() {\n        return userService.addUser();\n    }\n\n    @GetMapping(\"user/update\")\n    public void updateUser() {\n        userService.updateUser(new User(1L, \"mrbird\", \"123456\"));\n    }\n\n    @GetMapping(\"user/delete/{id:\\\\d+}\")\n    public void deleteUser(@PathVariable Long id) {\n        userService.deleteUser(id);\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/java/com/example/demo/filter/HystrixRequestContextServletFilter.java",
    "content": "package com.example.demo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\n@WebFilter(filterName = \"hystrixRequestContextServletFilter\", urlPatterns = \"/*\", asyncSupported = true)\npublic class HystrixRequestContextServletFilter implements Filter {\n    @Override\n    public void init(FilterConfig filterConfig) {\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        filterChain.doFilter(servletRequest, servletResponse);\n        context.close();\n    }\n\n    @Override\n    public void destroy() {\n    }\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Ribbon-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Ribbon-Consumer\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Turbine/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Turbine</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Turbine</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-turbine</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Turbine/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.turbine.EnableTurbine;\n\n@SpringBootApplication\n@EnableTurbine\n@EnableDiscoveryClient\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "31.Spring-Cloud-Hystrix-Dashboard-Turbine/Turbine/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: Turbine\nserver:\n  port: 9003\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\nturbine:\n  app-config: Ribbon-Consumer\n  cluster-name-expression: new String('default')\n  combine-host-port: true\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping(\"users\")\n    public List<User> get(String ids) {\n        log.info(\"批量获取用户信息\");\n        List<User> list = new ArrayList<>();\n        for (String id : ids.split(\",\")) {\n            list.add(new User(Long.valueOf(id), \"user\" + id, \"123456\"));\n        }\n        return list;\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Hystrix-Dashboard/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Hystrix-Dashboard</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Hystrix-Dashboard</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Hystrix-Dashboard/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;\n\n@SpringBootApplication\n@EnableHystrixDashboard\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Hystrix-Dashboard/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: Hystrix-Dashboard\nserver:\n  port: 9002\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Ribbon-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Ribbon-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-hystrix -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-hystrix</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-netflix-hystrix-stream</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.cloud.client.SpringCloudApplication;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringCloudApplication\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/java/com/example/demo/Service/UserService.java",
    "content": "package com.example.demo.Service;\n\nimport com.example.demo.domain.User;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport com.netflix.hystrix.contrib.javanica.command.AsyncResult;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n/**\n * @author MrBird\n */\n@Service(\"userService\")\npublic class UserService {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(batchMethod = \"findUserBatch\", collapserProperties = {\n            @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"100\")\n    })\n    public Future<User> findUser(Long id) {\n        log.info(\"获取单个用户信息\");\n        // return new AsyncResult<User>() {\n        //     @Override\n        //     public User invoke() {\n        //         return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n        //     }\n        // };\n        return null;\n    }\n\n    @HystrixCommand\n    public List<User> findUserBatch(List<Long> ids) {\n        log.info(\"批量获取用户信息,ids: \" + ids);\n        User[] users = restTemplate.getForObject(\"http://Server-Provider/user/users?ids={1}\", User[].class, StringUtils.join(ids, \",\"));\n        return Arrays.asList(users);\n    }\n\n    public String getCacheKey(Long id) {\n        return String.valueOf(id);\n    }\n\n    @CacheResult(cacheKeyMethod = \"getCacheKey\")\n    @HystrixCommand(fallbackMethod = \"getUserDefault\", commandKey = \"getUserById\", groupKey = \"userGroup\",\n            threadPoolKey = \"getUserThread\")\n    public User getUser(Long id) {\n        log.info(\"获取用户信息\");\n        return restTemplate.getForObject(\"http://Server-Provider/user/{id}\", User.class, id);\n    }\n\n    @HystrixCommand(fallbackMethod = \"getUserDefault2\")\n    public User getUserDefault(Long id) {\n        String a = null;\n        // 测试服务降级\n        a.toString();\n        User user = new User();\n        user.setId(-1L);\n        user.setUsername(\"defaultUser\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public User getUserDefault2(Long id, Throwable e) {\n        System.out.println(e.getMessage());\n        User user = new User();\n        user.setId(-2L);\n        user.setUsername(\"defaultUser2\");\n        user.setPassword(\"123456\");\n        return user;\n    }\n\n    public List<User> getUsers() {\n        return this.restTemplate.getForObject(\"http://Server-Provider/user\", List.class);\n    }\n\n    public String addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        HttpStatus status = this.restTemplate.postForEntity(\"http://Server-Provider/user\", user, null).getStatusCode();\n        if (status.is2xxSuccessful()) {\n            return \"新增用户成功\";\n        } else {\n            return \"新增用户失败\";\n        }\n    }\n\n    @CacheRemove(commandKey = \"getUserById\")\n    @HystrixCommand\n    public void updateUser(@CacheKey(\"id\") User user) {\n        this.restTemplate.put(\"http://Server-Provider/user\", user);\n    }\n\n    public void deleteUser(@PathVariable Long id) {\n        this.restTemplate.delete(\"http://Server-Provider/user/{1}\", id);\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.Service.UserService;\nimport com.example.demo.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"testRequestMerge\")\n    public void testRequerstMerge() throws InterruptedException, ExecutionException {\n        Future<User> f1 = userService.findUser(1L);\n        Future<User> f2 = userService.findUser(2L);\n        Future<User> f3 = userService.findUser(3L);\n        f1.get();\n        f2.get();\n        f3.get();\n        Thread.sleep(200);\n        Future<User> f4 = userService.findUser(4L);\n        f4.get();\n    }\n\n    @GetMapping(\"testCache\")\n    public void testCache() {\n        userService.getUser(1L);\n        userService.getUser(1L);\n        userService.getUser(1L);\n    }\n\n    @GetMapping(\"user/{id}\")\n    public User getUser(@PathVariable Long id) {\n        return userService.getUser(id);\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return userService.getUsers();\n    }\n\n    @GetMapping(\"user/add\")\n    public String addUser() {\n        return userService.addUser();\n    }\n\n    @GetMapping(\"user/update\")\n    public void updateUser() {\n        userService.updateUser(new User(1L, \"mrbird\", \"123456\"));\n    }\n\n    @GetMapping(\"user/delete/{id:\\\\d+}\")\n    public void deleteUser(@PathVariable Long id) {\n        userService.deleteUser(id);\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/java/com/example/demo/filter/HystrixRequestContextServletFilter.java",
    "content": "package com.example.demo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\n@WebFilter(filterName = \"hystrixRequestContextServletFilter\", urlPatterns = \"/*\", asyncSupported = true)\npublic class HystrixRequestContextServletFilter implements Filter {\n    @Override\n    public void init(FilterConfig filterConfig) {\n    }\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        filterChain.doFilter(servletRequest, servletResponse);\n        context.close();\n    }\n\n    @Override\n    public void destroy() {\n    }\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Ribbon-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Ribbon-Consumer\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Turbine-Stream/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Turbine-Stream</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Turbine-Stream</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-turbine-stream</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Turbine-Stream/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream;\n\n@SpringBootApplication\n@EnableTurbineStream\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n}\n"
  },
  {
    "path": "32.Spring-Cloud-Hystrix-Dashboard-Turbine-RabbitMQ/Turbine-Stream/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: Turbine\n  rabbitmq:\n      host: localhost\n      port: 5672\n      username: guest\n      password: guest\nserver:\n  port: 9003\n\n\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Feign-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Feign-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-feign</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport feign.Logger;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\n\n@EnableDiscoveryClient\n@EnableFeignClients\n@SpringBootApplication\npublic class DemoApplication {\n    @Bean\n    Logger.Level feignLoggerLevel() {\n        return Logger.Level.FULL;\n    }\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport com.example.demo.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"user/{id}\")\n    public User getUser(@PathVariable Long id) {\n        return userService.get(id);\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return userService.get();\n    }\n\n    @PostMapping(\"user\")\n    public void addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        userService.add(user);\n    }\n\n    @PutMapping(\"user\")\n    public void updateUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        userService.update(user);\n    }\n\n    @DeleteMapping(\"user/{id}\")\n    public void deleteUser(@PathVariable Long id) {\n        userService.delete(id);\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/java/com/example/demo/service/UserService.java",
    "content": "package com.example.demo.service;\n\nimport com.example.demo.domain.User;\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@FeignClient(value = \"Server-Provider\", fallback = UserServiceFallback.class)\npublic interface UserService {\n\n    @GetMapping(\"user/{id}\")\n    public User get(@PathVariable(\"id\") Long id);\n\n    @GetMapping(\"user\")\n    public List<User> get();\n\n    @PostMapping(\"user\")\n    public void add(@RequestBody User user);\n\n    @PutMapping(\"user\")\n    public void update(@RequestBody User user);\n\n    @DeleteMapping(\"user/{id}\")\n    public void delete(@PathVariable(\"id\") Long id);\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/java/com/example/demo/service/UserServiceFallback.java",
    "content": "package com.example.demo.service;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class UserServiceFallback implements UserService {\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public User get(Long id) {\n        return new User(-1L, \"default\", \"123456\");\n    }\n\n    @Override\n    public List<User> get() {\n        return null;\n    }\n\n    @Override\n    public void add(User user) {\n        log.info(\"test fallback\");\n    }\n\n    @Override\n    public void update(User user) {\n        log.info(\"test fallback\");\n    }\n\n    @Override\n    public void delete(Long id) {\n        log.info(\"test fallback\");\n    }\n}\n"
  },
  {
    "path": "33.Spring-Cloud-Feign-Declarative-REST-Client/Feign-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Server-Consumer\n    \neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\nfeign:\n  hystrix:\n    enabled: true\nlogging:\n  level:\n    com:\n      example:\n        demo:\n          service:\n            UserService: debug\n"
  },
  {
    "path": "34.Start-Spring-Security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- spring-boot-starter-security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "34.Start-Spring-Security/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "34.Start-Spring-Security/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.formLogin() // 表单登录\n        // http.httpBasic() // HTTP Basic\n                .and()\n                .authorizeRequests() // 授权配置\n                .anyRequest()  // 所有请求\n                .authenticated(); // 都需要认证\n    }\n}\n"
  },
  {
    "path": "34.Start-Spring-Security/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n}\n"
  },
  {
    "path": "34.Start-Spring-Security/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true"
  },
  {
    "path": "35.Spring-Security-Authentication/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- spring-boot-starter-security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.formLogin() // 表单登录\n                // http.httpBasic() // HTTP Basic\n                .loginPage(\"/authentication/require\") // 登录跳转 URL\n                .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                .successHandler(authenticationSucessHandler) // 处理登录成功\n                .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                .authorizeRequests() // 授权配置\n                .antMatchers(\"/authentication/require\", \"/login.html\").permitAll() // 登录跳转 URL 无需认证\n                .anyRequest()  // 所有请求\n                .authenticated() // 都需要认证\n                .and().csrf().disable();\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "35.Spring-Security-Authentication/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "36.Spring-Security-ValidateCode/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n\n        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n                .formLogin() // 表单登录\n                // http.httpBasic() // HTTP Basic\n                .loginPage(\"/authentication/require\") // 登录跳转 URL\n                .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                .successHandler(authenticationSucessHandler) // 处理登录成功\n                .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                .authorizeRequests() // 授权配置\n                .antMatchers(\"/authentication/require\",\n                        \"/login.html\",\n                        \"/code/image\").permitAll() // 无需认证的请求路径\n                .anyRequest()  // 所有请求\n                .authenticated() // 都需要认证\n                .and().csrf().disable();\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.time.LocalDateTime;\n\npublic class ImageCode {\n\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, imageCode);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "36.Spring-Security-ValidateCode/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "37.Spring-Security-RememberMe/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;\nimport org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Autowired\n    private DataSource dataSource;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Bean\n    public PersistentTokenRepository persistentTokenRepository() {\n        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();\n        jdbcTokenRepository.setDataSource(dataSource);\n        jdbcTokenRepository.setCreateTableOnStartup(false);\n        return jdbcTokenRepository;\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n\n        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n                .formLogin() // 表单登录\n                    // http.httpBasic() // HTTP Basic\n                    .loginPage(\"/authentication/require\") // 登录跳转 URL\n                    .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                    .successHandler(authenticationSucessHandler) // 处理登录成功\n                    .failureHandler(authenticationFailureHandler) // 处理登录失败\n                    .and()\n                .rememberMe()\n                    .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库\n                    .tokenValiditySeconds(3600) // remember 过期时间，单为秒\n                    .userDetailsService(userDetailService) // 处理自动登录逻辑\n                .and()\n                    .authorizeRequests() // 授权配置\n                    .antMatchers(\"/authentication/require\",\n                            \"/login.html\",\n                            \"/code/image\").permitAll() // 无需认证的请求路径\n                    .anyRequest()  // 所有请求\n                    .authenticated() // 都需要认证\n                .and()\n                    .csrf().disable();\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.time.LocalDateTime;\n\npublic class ImageCode {\n\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, imageCode);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n\n\n\nspring:\n  datasource:\n    driver-class-name: com.mysql.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/security?useUnicode=yes&characterEncoding=UTF-8&useSSL=false\n    username: root\n    password: 123456"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "37.Spring-Security-RememberMe/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <input type=\"checkbox\" name=\"remember-me\"/> 记住我\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "38.Spring-Security-SmsCode/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport cc.mrbird.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.validate.smscode.SmsAuthenticationFilter;\nimport cc.mrbird.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n\n        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n            .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                    // http.httpBasic() // HTTP Basic\n                    .loginPage(\"/authentication/require\") // 登录跳转 URL\n                    .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                    .successHandler(authenticationSucessHandler) // 处理登录成功\n                    .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                    .authorizeRequests() // 授权配置\n                    .antMatchers(\"/authentication/require\",\n                            \"/login.html\", \"/code/image\",\"/code/sms\").permitAll() // 无需认证的请求路径\n                    .anyRequest()  // 所有请求\n                    .authenticated() // 都需要认证\n                .and()\n                    .csrf().disable()\n                .apply(smsAuthenticationConfig); // 将短信验证码认证配置加到 Spring Security 中\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.time.LocalDateTime;\n\npublic class ImageCode {\n\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    public ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport java.time.LocalDateTime;\n\npublic class SmsCode {\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public SmsCode(String code, int expireIn) {\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public SmsCode(String code, LocalDateTime expireTime) {\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.validate.code.ValidateCodeException;\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n\n        SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n}"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport cc.mrbird.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    public final static String SESSION_KEY_SMS_CODE = \"SESSION_KEY_SMS_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, imageCode);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) {\n        SmsCode smsCode = createSMSCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_SMS_CODE + mobile, smsCode);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"您的登录验证码为：\" + smsCode.getCode() + \"，有效时间为60秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code, 60);\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "38.Spring-Security-SmsCode/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n\n<form class=\"login-page\" action=\"/login/mobile\" method=\"post\">\n    <div class=\"form\">\n        <h3>短信验证码登录</h3>\n        <input type=\"text\" placeholder=\"手机号\" name=\"mobile\" value=\"17777777777\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"smsCode\" placeholder=\"短信验证码\" style=\"width: 50%;\"/>\n            <a href=\"/code/sms?mobile=17777777777\">发送验证码</a>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Client</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient client;\n\n    @GetMapping(\"/info\")\n    public String info() {\n        @SuppressWarnings(\"deprecation\")\n        ServiceInstance instance = client.getLocalServiceInstance();\n        String info = \"host：\" + instance.getHost() + \"，service_id：\" + instance.getServiceId();\n        log.info(info);\n        return info;\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/src/main/java/com/example/demo/controller/UserController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"/{id:\\\\d+}\")\n    public User get(@PathVariable Long id) {\n        log.info(\"获取用户id为 \" + id + \"的信息\");\n        return new User(id, \"mrbird\", \"123456\");\n    }\n\n    @GetMapping\n    public List<User> get() {\n        List<User> list = new ArrayList<>();\n        list.add(new User(1L, \"mrbird\", \"123456\"));\n        list.add(new User(2L, \"scott\", \"123456\"));\n        log.info(\"获取用户信息 \" + list);\n        return list;\n    }\n\n    @PostMapping\n    public void add(@RequestBody User user) {\n        log.info(\"新增用户成功 \" + user);\n    }\n\n    @PutMapping\n    public void update(@RequestBody User user) {\n        log.info(\"更新用户成功 \" + user);\n    }\n\n    @DeleteMapping(\"/{id:\\\\d+}\")\n    public void delete(@PathVariable Long id) {\n        log.info(\"删除用户成功 \" + id);\n    }\n\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n\n    private String username;\n\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Client/src/main/resources/application.yml",
    "content": "server:\n  port: 8082\n  \nspring:\n  application:\n    name: Server-Provider\n    \neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Feign-Consumer</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Feign-Consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-feign</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport feign.Logger;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.feign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\n\n@EnableDiscoveryClient\n@EnableFeignClients\n@SpringBootApplication\npublic class DemoApplication {\n    @Bean\n    Logger.Level feignLoggerLevel() {\n        return Logger.Level.FULL;\n    }\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport com.example.demo.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\npublic class TestController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"user/{id}\")\n    public User getUser(@PathVariable Long id) {\n        return userService.get(id);\n    }\n\n    @GetMapping(\"user\")\n    public List<User> getUsers() {\n        return userService.get();\n    }\n\n    @PostMapping(\"user\")\n    public void addUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        userService.add(user);\n    }\n\n    @PutMapping(\"user\")\n    public void updateUser() {\n        User user = new User(1L, \"mrbird\", \"123456\");\n        userService.update(user);\n    }\n\n    @DeleteMapping(\"user/{id}\")\n    public void deleteUser(@PathVariable Long id) {\n        userService.delete(id);\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = 1339434510787399891L;\n    private Long id;\n    private String username;\n    private String password;\n\n    public User() {\n    }\n\n    public User(Long id, String username, String password) {\n        this.id = id;\n        this.username = username;\n        this.password = password;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    @Override\n    public String toString() {\n        return \"User{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/java/com/example/demo/service/UserService.java",
    "content": "package com.example.demo.service;\n\nimport com.example.demo.domain.User;\nimport org.springframework.cloud.netflix.feign.FeignClient;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@FeignClient(value = \"Server-Provider\", fallback = UserServiceFallback.class)\npublic interface UserService {\n\n    @GetMapping(\"user/{id}\")\n    public User get(@PathVariable(\"id\") Long id);\n\n    @GetMapping(\"user\")\n    public List<User> get();\n\n    @PostMapping(\"user\")\n    public void add(@RequestBody User user);\n\n    @PutMapping(\"user\")\n    public void update(@RequestBody User user);\n\n    @DeleteMapping(\"user/{id}\")\n    public void delete(@PathVariable(\"id\") Long id);\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/java/com/example/demo/service/UserServiceFallback.java",
    "content": "package com.example.demo.service;\n\nimport com.example.demo.domain.User;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class UserServiceFallback implements UserService {\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public User get(Long id) {\n        return new User(-1L, \"default\", \"123456\");\n    }\n\n    @Override\n    public List<User> get() {\n        return null;\n    }\n\n    @Override\n    public void add(User user) {\n        log.info(\"test fallback\");\n    }\n\n    @Override\n    public void update(User user) {\n        log.info(\"test fallback\");\n    }\n\n    @Override\n    public void delete(Long id) {\n        log.info(\"test fallback\");\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Feign-Consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\n  \nspring:\n  application:\n    name: Server-Consumer\n    \neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\nfeign:\n  hystrix:\n    enabled: true\nlogging:\n  level:\n    com:\n      example:\n        demo:\n          service:\n            UserService: debug\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Zuul-Gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Zuul-Gateway</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zuul</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Zuul-Gateway/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@EnableZuulProxy\n@EnableDiscoveryClient\n@SpringBootApplication\n@RestController\npublic class DemoApplication {\n    @GetMapping(\"/test/hello\")\n    public String hello() {\n        return \"hello zuul\";\n    }\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Zuul-Gateway/src/main/java/com/example/demo/filter/PreSendForwardFilter.java",
    "content": "package com.example.demo.filter;\n\nimport com.netflix.zuul.ZuulFilter;\nimport com.netflix.zuul.context.RequestContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class PreSendForwardFilter extends ZuulFilter {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public String filterType() {\n        return \"pre\";\n    }\n\n    @Override\n    public int filterOrder() {\n        return 1;\n    }\n\n    @Override\n    public boolean shouldFilter() {\n        return true;\n    }\n\n    @Override\n    public Object run() {\n        RequestContext requestContext = RequestContext.getCurrentContext();\n        HttpServletRequest request = requestContext.getRequest();\n        String host = request.getRemoteHost();\n        String method = request.getMethod();\n        String uri = request.getRequestURI();\n        log.info(\"请求URI：{}，HTTP Method：{}，请求IP：{}\", uri, method, host);\n        return null;\n    }\n}\n"
  },
  {
    "path": "39.Spring-Cloud-Zuul-Router/Zuul-Gateway/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: Zuul-Gateway\nserver:\n  port: 12580\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\nzuul:\n  routes:\n    api-a:\n      path: /api-a/**\n      url: http://localhost:8082\n    api-b:\n      path: /api-b/**\n      serviceId: server-provider\n    api-c:\n      path: /api-c/**\n      serviceId: server-consumer\n    api-d:\n      path: /api-c/user/1\n      serviceId: lol\n    api-e:\n      path: /api-e/**\n      url: forward:/test\n  ignored-services: server-consumer\n  sensitive-headers:\n  add-host-header: true"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/common-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>common-api</artifactId>\n\n</project>"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/common-api/src/main/java/cc/mrbird/common/api/HelloService.java",
    "content": "package cc.mrbird.common.api;\n\npublic interface HelloService {\n    String hello(String message);\n}\n"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>dubbo-boot</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <name>dubbo-boot</name>\n    <description>Spring Boot-Dubbo-ZooKeeper</description>\n\n    <modules>\n        <module>common-api</module>\n        <module>server-provider</module>\n        <module>server-consumer</module>\n    </modules>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <project.version>1.0</project.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- dubbo -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>0.2.0</version>\n        </dependency>\n\n        <!-- zookeeper -->\n        <dependency>\n            <groupId>org.apache.zookeeper</groupId>\n            <artifactId>zookeeper</artifactId>\n            <version>3.4.8</version>\n        </dependency>\n        <dependency>\n            <groupId>com.101tec</groupId>\n            <artifactId>zkclient</artifactId>\n            <version>0.10</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-consumer</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-consumer/src/main/java/cc/mrbird/ConsumerApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@EnableDubbo\n@SpringBootApplication\npublic class ConsumerApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplicaiton.class, args);\n    }\n}\n"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-consumer/src/main/java/cc/mrbird/consumer/controller/HelloController.java",
    "content": "package cc.mrbird.consumer.controller;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HelloController {\n\n    @Reference\n    private HelloService helloService;\n\n    @GetMapping(\"/hello/{message}\")\n    public String hello(@PathVariable String message) {\n        return this.helloService.hello(message);\n    }\n}\n"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-consumer\n    # zookeeper地址，用于从中获取注册的服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-provider</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-provider/src/main/java/cc/mrbird/ProviderApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@EnableDubbo\n@SpringBootApplication\npublic class ProviderApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplicaiton.class, args);\n        System.out.println(\"complete\");\n    }\n}\n"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-provider/src/main/java/cc/mrbird/provider/service/HelloServiceImpl.java",
    "content": "package cc.mrbird.provider.service;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Service;\nimport org.springframework.stereotype.Component;\n\n@Service(interfaceClass = HelloService.class)\n@Component\npublic class HelloServiceImpl implements HelloService {\n    @Override\n    public String hello(String message) {\n        return \"hello,\" + message;\n    }\n}\n"
  },
  {
    "path": "40.Spring-Boot-Dubbo-Zookeeper/server-provider/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-provider\n    # zookeeper地址，用于向其注册服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  #暴露服务方式\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo\n    # 暴露服务端口 （默认是20880，不同的服务提供者端口不能重复）\n    port: 20880"
  },
  {
    "path": "41.Spring-Cloud-Config/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "41.Spring-Cloud-Config/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "41.Spring-Cloud-Config/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "41.Spring-Cloud-Config/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "41.Spring-Cloud-Config/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "41.Spring-Cloud-Config/config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Config-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <!--<dependency>-->\n            <!--<groupId>org.springframework.cloud</groupId>-->\n            <!--<artifactId>spring-cloud-starter-eureka</artifactId>-->\n        <!--</dependency>-->\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-client/src/main/java/cc/mrbird/demo/DemoApplication.java",
    "content": "package cc.mrbird.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-client/src/main/java/cc/mrbird/demo/controller/TestController.java",
    "content": "package cc.mrbird.demo.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RefreshScope\npublic class TestController {\n\n    @Value(\"${message}\")\n    private String message;\n\n    @GetMapping(\"message\")\n    public String getMessage() {\n        return this.message;\n    }\n}\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-client/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: febs\n  cloud:\n    config:\n      profile: dev\n      label: master\n      uri: http://localhost:12580\n      username: mrbird\n      password: 123456\n#      discovery:\n#        enabled: true\n#        service-id: config-server\n\n#eureka:\n#  client:\n#    serviceUrl:\n#      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\nserver:\n  port: 12581\nmanagement:\n  security:\n    enabled: false\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Config-Server</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <!--<dependency>-->\n            <!--<groupId>org.springframework.cloud</groupId>-->\n            <!--<artifactId>spring-cloud-starter-eureka</artifactId>-->\n        <!--</dependency>-->\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-server/src/main/java/cc/mrbird/demo/DemoApplication.java",
    "content": "package cc.mrbird.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\n// @EnableDiscoveryClient\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "41.Spring-Cloud-Config/config-server/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: config-server\n  cloud:\n    config:\n      server:\n        git:\n          uri: xxx\n          username: xxx\n          password: xxx\n#          search-paths: '{application}'\n          clone-on-start: true\n\nserver:\n  port: 12580\nsecurity:\n  user:\n    name: mrbird\n    password: 123456\n\n#eureka:\n#  client:\n#    serviceUrl:\n#      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "41.Spring-Cloud-Config/config-server/src/main/resources/bootstrap.yml",
    "content": "#encrypt:\n##  key: hello\n\n#encrypt:\n#  key-store:\n#    location: classpath:config-server.keystore\n#    alias: Config-Server\n#    password: 123456\n#    secret: 654321"
  },
  {
    "path": "41.Spring-Cloud-Config/properties/febs-dev.yml",
    "content": "message: 'dev properties (master v1.0)'"
  },
  {
    "path": "41.Spring-Cloud-Config/properties/febs-pro.yml",
    "content": "message: 'pro properties (master v1.0)'"
  },
  {
    "path": "41.Spring-Cloud-Config/properties/febs-test.yml",
    "content": "message: 'test properties (master v1.0)'"
  },
  {
    "path": "41.Spring-Cloud-Config/properties/febs.yml",
    "content": "message: 'default properties (master v1.0)'"
  },
  {
    "path": "42.Spring-Cloud-Bus/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Config-Client</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-client/src/main/java/cc/mrbird/demo/DemoApplication.java",
    "content": "package cc.mrbird.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-client/src/main/java/cc/mrbird/demo/controller/TestController.java",
    "content": "package cc.mrbird.demo.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RefreshScope\npublic class TestController {\n\n    @Value(\"${message}\")\n    private String message;\n\n    @GetMapping(\"message\")\n    public String getMessage() {\n        return this.message;\n    }\n}\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-client/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: febs\n  cloud:\n    config:\n      profile: dev\n      label: master\n      uri: http://localhost:12580\n      username: mrbird\n      password: 123456\n      discovery:\n        enabled: true\n        service-id: config-server\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n\nserver:\n  port: 12581\nmanagement:\n  security:\n    enabled: false\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Config-Server</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-server/src/main/java/cc/mrbird/demo/DemoApplication.java",
    "content": "package cc.mrbird.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\n@EnableDiscoveryClient\npublic class DemoApplication {\n\n    private String a;\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-server/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: config-server\n  cloud:\n    config:\n      server:\n        git:\n          uri: xxx\n          username: xxx\n          password: xxx\n          search-paths: '{application}'\n          clone-on-start: true\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\nserver:\n  port: 12580\nsecurity:\n  user:\n    name: mrbird\n    password: 123456\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/\n"
  },
  {
    "path": "42.Spring-Cloud-Bus/config-server/src/main/resources/bootstrap.yml",
    "content": "#encrypt:\n##  key: hello\n\n#encrypt:\n#  key-store:\n#    location: classpath:config-server.keystore\n#    alias: Config-Server\n#    password: 123456\n#    secret: 654321"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Eureka-Service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>Eureka-Service</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Eureka-Service</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>nexus-aliyun</id>\n            <name>Nexus aliyun</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Eureka-Service/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n\n@EnableEurekaServer\n@SpringBootApplication\npublic class DemoApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Eureka-Service/src/main/resources/application-peer1.yml",
    "content": "server:\n  port: 8080\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer1\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer2:8081/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Eureka-Service/src/main/resources/application-peer2.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: Eureka-Server\n\neureka:\n  instance:\n    hostname: peer2\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/\n  server:\n    enable-self-preservation: false\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Eureka-Service/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n  user:\n    name: mrbird\n    password: 123456\nspring:\n  profiles:\n    active: peer1"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider1/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Server-Provider1</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-sleuth</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-sleuth-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider1/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoApplication {\n\n    @Bean\n    @LoadBalanced\n    RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider1/src/main/java/com/example/demo/controller/HelloController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"hello\")\npublic class HelloController {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping\n    public String hello() {\n        logger.info(\"调用server-provider1的hello接口\");\n        return this.restTemplate.getForEntity(\"http://server-provider2/hello\", String.class).getBody();\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider1/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: server-provider1\n  zipkin:\n    base-url: http://localhost:9100\n\n  sleuth:\n    sampler:\n      percentage: 1\n\nserver:\n  port: 9000\n\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Server-Provider2</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-eureka</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-ribbon</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-sleuth</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-sleuth-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider2/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider2/src/main/java/com/example/demo/controller/HelloController.java",
    "content": "package com.example.demo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"hello\")\npublic class HelloController {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping\n    public String hello() {\n        logger.info(\"调用server-provider2的hello接口\");\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Server-Provider2/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: server-provider2\n  zipkin:\n    base-url: http://localhost:9100\n\n  sleuth:\n    sampler:\n      percentage: 1\n\nserver:\n  port: 9001\n\neureka:\n  client:\n    serviceUrl:\n      defaultZone: http://mrbird:123456@peer1:8080/eureka/,http://mrbird:123456@peer2:8081/eureka/"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Zipkin-Server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Zipkin-Server</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <packaging>jar</packaging>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.13.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>Edgware.SR3</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.java</groupId>\n            <artifactId>zipkin-server</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.java</groupId>\n            <artifactId>zipkin-autoconfigure-ui</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.java</groupId>\n            <artifactId>zipkin-autoconfigure-storage-mysql</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Zipkin-Server/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;\n\n@SpringBootApplication\n// @EnableZipkinServer\n@EnableZipkinStreamServer\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "43.Spring-Cloud-Sleuth/Zipkin-Server/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: zipkin-server\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/zipkin?useUnicode=true&characterEncoding=utf-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password: 123456\n\nserver:\n  port: 9100\n\nzipkin:\n  storage:\n    type: mysql"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>autoconfig</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/annotation/EnableHelloWorld.java",
    "content": "package com.example.demo.annotation;\n\nimport com.example.demo.configuration.HelloWorldConfiguration;\nimport org.springframework.context.annotation.Import;\n\nimport java.lang.annotation.*;\n\n/**\n * @author MrBird\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n// @Import(HelloWorldImportSelector.class)\n@Import(HelloWorldConfiguration.class)\npublic @interface EnableHelloWorld {\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/annotation/FirstLevelService.java",
    "content": "package com.example.demo.annotation;\n\nimport org.springframework.stereotype.Repository;\nimport org.springframework.stereotype.Service;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Service\npublic @interface FirstLevelService {\n    String value() default \"\";\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/annotation/SecondLevelService.java",
    "content": "package com.example.demo.annotation;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@FirstLevelService\npublic @interface SecondLevelService {\n    String value() default \"\";\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/bootstrap/EnableAutoConfigurationBootstrap.java",
    "content": "package com.example.demo.bootstrap;\n\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.context.ConfigurableApplicationContext;\n\n/**\n * @author MrBird\n */\n@EnableAutoConfiguration\npublic class EnableAutoConfigurationBootstrap {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)\n                .web(WebApplicationType.NONE)\n                .run(args);\n        String hello = context.getBean(\"hello\", String.class);\n        System.out.println(\"hello Bean: \" + hello);\n        context.close();\n    }\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/bootstrap/ServiceBootstrap.java",
    "content": "package com.example.demo.bootstrap;\n\nimport com.example.demo.service.TestService;\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\n\n/**\n * @author MrBird\n */\n@ComponentScan(\"com.example.demo.service\")\npublic class ServiceBootstrap {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = new SpringApplicationBuilder(ServiceBootstrap.class)\n                .web(WebApplicationType.NONE)\n                .run(args);\n        TestService testService = context.getBean(\"testService\", TestService.class);\n        System.out.println(\"TestService Bean: \" + testService);\n        context.close();\n    }\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/bootstrap/TestEnableBootstap.java",
    "content": "package com.example.demo.bootstrap;\n\nimport com.example.demo.annotation.EnableHelloWorld;\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.context.ConfigurableApplicationContext;\n\n/**\n * @author MrBird\n */\n@EnableHelloWorld\npublic class TestEnableBootstap {\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context = new SpringApplicationBuilder(TestEnableBootstap.class)\n                .web(WebApplicationType.NONE)\n                .run(args);\n        String hello = context.getBean(\"hello\", String.class);\n        System.out.println(\"hello Bean: \" + hello);\n        context.close();\n    }\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/configuration/HelloWorldAutoConfiguration.java",
    "content": "package com.example.demo.configuration;\n\nimport com.example.demo.annotation.EnableHelloWorld;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableHelloWorld\n@ConditionalOnProperty(name = \"helloworld\", havingValue = \"true\")\npublic class HelloWorldAutoConfiguration {\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/configuration/HelloWorldConfiguration.java",
    "content": "package com.example.demo.configuration;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class HelloWorldConfiguration {\n\n    @Bean\n    public String hello() {\n        return \"hello world\";\n    }\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/selector/HelloWorldImportSelector.java",
    "content": "package com.example.demo.selector;\n\nimport com.example.demo.configuration.HelloWorldConfiguration;\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\n\n/**\n * @author MrBird\n */\npublic class HelloWorldImportSelector implements ImportSelector {\n    @Override\n    public String[] selectImports(AnnotationMetadata importingClassMetadata) {\n        return new String[]{HelloWorldConfiguration.class.getName()};\n    }\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/java/com/example/demo/service/TestService.java",
    "content": "package com.example.demo.service;\n\nimport com.example.demo.annotation.FirstLevelService;\nimport com.example.demo.annotation.SecondLevelService;\n\n/**\n * @author MrBird\n */\n@SecondLevelService\npublic class TestService {\n}\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/resources/META-INF/spring.factories",
    "content": "# Auto Configure\norg.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\ncom.example.demo.configuration.HelloWorldAutoConfiguration"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/main/resources/application.properties",
    "content": "helloworld=true\n"
  },
  {
    "path": "44.Spring-Boot-Autoconfiguration/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>SpringApplication</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.Banner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // SpringApplication application = new SpringApplication(DemoApplication.class);\n        // application.setBannerMode(Banner.Mode.OFF);\n        // application.setWebApplicationType(WebApplicationType.NONE);\n        // application.setHeadless(true);\n        // application.run(args);\n\n        new SpringApplicationBuilder(DemoApplication.class)\n                .web(WebApplicationType.NONE)\n                .run(args);\n    }\n\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/initializer/AfterHelloApplicationContextInitializer.java",
    "content": "package com.example.demo.initializer;\n\nimport org.springframework.context.ApplicationContextInitializer;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.Ordered;\n\n/**\n * @author MrBird\n */\npublic class AfterHelloApplicationContextInitializer\n        implements ApplicationContextInitializer, Ordered {\n    @Override\n    public void initialize(ConfigurableApplicationContext applicationContext) {\n        System.out.println(\"AfterHelloApplicationContextInitializer: \" + applicationContext.getId());\n    }\n\n    @Override\n    public int getOrder() {\n        return Ordered.LOWEST_PRECEDENCE;\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/initializer/HelloApplicationContextInitializer.java",
    "content": "package com.example.demo.initializer;\n\nimport org.springframework.context.ApplicationContextInitializer;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\n\n/**\n * @author MrBird\n */\n@Order(Ordered.HIGHEST_PRECEDENCE)\npublic class HelloApplicationContextInitializer\n        implements ApplicationContextInitializer {\n    @Override\n    public void initialize(ConfigurableApplicationContext applicationContext) {\n        System.out.println(\"ConfigurableApplicationContext.id - \" + applicationContext.getId());\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/listener/AfterContextClosedEventListener.java",
    "content": "package com.example.demo.listener;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.core.Ordered;\n\n/**\n * @author MrBird\n */\npublic class AfterContextClosedEventListener implements ApplicationListener<ContextClosedEvent>, Ordered {\n    @Override\n    public void onApplicationEvent(ContextClosedEvent event) {\n        System.out.println(\"AfterContextClosedEvent: \" + event.getApplicationContext().getId());\n    }\n\n    @Override\n    public int getOrder() {\n        return Ordered.HIGHEST_PRECEDENCE + 1;\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/listener/ContextClosedEventListener.java",
    "content": "package com.example.demo.listener;\n\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\n\n/**\n * @author MrBird\n */\n@Order(Ordered.HIGHEST_PRECEDENCE)\npublic class ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> {\n\n    @Override\n    public void onApplicationEvent(ContextClosedEvent event) {\n        System.out.println(\"ContextClosedEvent: \" + event.getApplicationContext().getId());\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/listener/HelloApplicationRunListener.java",
    "content": "package com.example.demo.listener;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.SpringApplicationRunListener;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.ConfigurableEnvironment;\n\n/**\n * @author MrBird\n */\npublic class HelloApplicationRunListener implements SpringApplicationRunListener {\n\n\n    public HelloApplicationRunListener(SpringApplication application, String[] args) {\n    }\n\n\n    @Override\n    public void starting() {\n        System.out.println(\"HelloApplicationRunListener starting......\");\n    }\n\n    @Override\n    public void environmentPrepared(ConfigurableEnvironment environment) {\n\n    }\n\n    @Override\n    public void contextPrepared(ConfigurableApplicationContext context) {\n\n    }\n\n    @Override\n    public void contextLoaded(ConfigurableApplicationContext context) {\n\n    }\n\n    @Override\n    public void started(ConfigurableApplicationContext context) {\n\n    }\n\n    @Override\n    public void running(ConfigurableApplicationContext context) {\n\n    }\n\n    @Override\n    public void failed(ConfigurableApplicationContext context, Throwable exception) {\n\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/runner/HelloApplicationRunner.java",
    "content": "package com.example.demo.runner;\n\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class HelloApplicationRunner implements ApplicationRunner {\n    @Override\n    public void run(ApplicationArguments args) {\n        System.out.println(\"HelloApplicationRunner: hello spring boot\");\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/java/com/example/demo/runner/HelloCommandLineRunner.java",
    "content": "package com.example.demo.runner;\n\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class HelloCommandLineRunner implements CommandLineRunner {\n    @Override\n    public void run(String... args) {\n        System.out.println(\"HelloCommandLineRunner: hello spring boot\");\n    }\n}\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/resources/META-INF/spring.factories",
    "content": "# Initializers\norg.springframework.context.ApplicationContextInitializer=\\\ncom.example.demo.initializer.HelloApplicationContextInitializer,\\\ncom.example.demo.initializer.AfterHelloApplicationContextInitializer\n\n# Application Listeners\norg.springframework.context.ApplicationListener=\\\ncom.example.demo.listener.ContextClosedEventListener,\\\ncom.example.demo.listener.AfterContextClosedEventListener\n\n# Run Listeners\norg.springframework.boot.SpringApplicationRunListener=\\\ncom.example.demo.listener.HelloApplicationRunListener"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "45.Spring-Boot-SpringApplication/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>validator</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.User;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\n\n/**\n * @author MrBird\n */\n@RestController\n@Validated\npublic class TestController {\n\n    @GetMapping(\"test1\")\n    public String test1(\n            @NotBlank(message = \"{required}\") String name,\n            @Email(message = \"{invalid}\") String email) {\n        return \"success\";\n    }\n\n    @GetMapping(\"test2\")\n    public String test2(@Valid User user) {\n        return \"success\";\n    }\n}\n\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * @author MrBird\n */\npublic class User implements Serializable {\n    private static final long serialVersionUID = -2731598327208972274L;\n\n    @NotBlank(message = \"{required}\")\n    private String name;\n\n    @Email(message = \"{invalid}\")\n    private String email;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        this.email = email;\n    }\n}\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/java/com/example/demo/handler/GlobalExceptionHandler.java",
    "content": "package com.example.demo.handler;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.validation.BindException;\nimport org.springframework.validation.FieldError;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.Path;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @author MrBird\n */\n@RestControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\n    /**\n     * 统一处理请求参数校验(普通传参)\n     *\n     * @param e ConstraintViolationException\n     * @return FebsResponse\n     */\n    @ExceptionHandler(value = ConstraintViolationException.class)\n    @ResponseStatus(HttpStatus.BAD_REQUEST)\n    public String handleConstraintViolationException(ConstraintViolationException e) {\n        StringBuilder message = new StringBuilder();\n        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();\n        for (ConstraintViolation<?> violation : violations) {\n            Path path = violation.getPropertyPath();\n            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), \".\");\n            message.append(pathArr[1]).append(violation.getMessage()).append(\",\");\n        }\n        message = new StringBuilder(message.substring(0, message.length() - 1));\n        return message.toString();\n    }\n\n    /**\n     * 统一处理请求参数校验(实体对象传参)\n     *\n     * @param e BindException\n     * @return FebsResponse\n     */\n    @ExceptionHandler(BindException.class)\n    @ResponseStatus(HttpStatus.BAD_REQUEST)\n    public String validExceptionHandler(BindException e) {\n        StringBuilder message = new StringBuilder();\n        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();\n        for (FieldError error : fieldErrors) {\n            message.append(error.getField()).append(error.getDefaultMessage()).append(\",\");\n        }\n        message = new StringBuilder(message.substring(0, message.length() - 1));\n        return message.toString();\n\n    }\n}\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/resources/ValidationMessages.properties",
    "content": "required=\\u4e0d\\u80fd\\u4e3a\\u7a7a\ninvalid=\\u683c\\u5f0f\\u4e0d\\u5408\\u6cd5"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "46.Spring-Boot-Hibernate-Validator/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/config/WebConfigurer.java",
    "content": "package com.example.demo.config;\n\nimport com.example.demo.converter.PropertiesHttpMessageConverter;\nimport com.example.demo.handler.PropertiesHandlerMethodReturnValueHandler;\nimport com.example.demo.resolver.PropertiesHandlerMethodArgumentResolver;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.HandlerMethodReturnValueHandler;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\nimport org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;\n\nimport javax.annotation.PostConstruct;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class WebConfigurer implements WebMvcConfigurer {\n\n\n    @Autowired\n    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;\n\n    @PostConstruct\n    public void init() {\n        // 获取当前 RequestMappingHandlerAdapter 所有的 ArgumentResolver对象\n        List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();\n        List<HandlerMethodArgumentResolver> newArgumentResolvers = new ArrayList<>(argumentResolvers.size() + 1);\n        // 添加 PropertiesHandlerMethodArgumentResolver 到集合第一个位置\n        newArgumentResolvers.add(0, new PropertiesHandlerMethodArgumentResolver());\n        // 将原 ArgumentResolver 添加到集合中\n        newArgumentResolvers.addAll(argumentResolvers);\n        // 重新设置 ArgumentResolver对象集合\n        requestMappingHandlerAdapter.setArgumentResolvers(newArgumentResolvers);\n\n        // 获取当前 RequestMappingHandlerAdapter 所有的 returnValueHandlers对象\n        List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();\n        List<HandlerMethodReturnValueHandler> newReturnValueHandlers = new ArrayList<>(returnValueHandlers.size() + 1);\n        // 添加 PropertiesHandlerMethodReturnValueHandler 到集合第一个位置\n        newReturnValueHandlers.add(0, new PropertiesHandlerMethodReturnValueHandler());\n        // 将原 returnValueHandlers 添加到集合中\n        newReturnValueHandlers.addAll(returnValueHandlers);\n        // 重新设置 ReturnValueHandlers对象集合\n        requestMappingHandlerAdapter.setReturnValueHandlers(newReturnValueHandlers);\n    }\n\n    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {\n        // converters.add(new PropertiesHttpMessageConverter());\n        // 指定顺序，这里为第一个\n        converters.add(0, new PropertiesHttpMessageConverter());\n    }\n\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Properties;\n\n/**\n * @author MrBird\n */\n// @RestController\n@Controller\npublic class TestController {\n\n    @GetMapping(value = \"test\", consumes = \"text/properties\")\n    @ResponseBody\n    public Properties getUser(@RequestBody Properties properties) {\n        return properties;\n    }\n\n    @GetMapping(value = \"test1\", consumes = \"text/properties\")\n    // @ResponseBody\n    public Properties getUser1(Properties properties) {\n        return properties;\n    }\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/converter/PropertiesHttpMessageConverter.java",
    "content": "package com.example.demo.converter;\n\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpInputMessage;\nimport org.springframework.http.HttpOutputMessage;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.AbstractGenericHttpMessageConverter;\nimport org.springframework.http.converter.HttpMessageNotReadableException;\nimport org.springframework.http.converter.HttpMessageNotWritableException;\n\nimport java.io.*;\nimport java.lang.reflect.Type;\nimport java.nio.charset.Charset;\nimport java.util.Properties;\n\n/**\n * @author MrBird\n */\npublic class PropertiesHttpMessageConverter extends AbstractGenericHttpMessageConverter<Properties> {\n\n    public PropertiesHttpMessageConverter() {\n        super(new MediaType(\"text\", \"properties\"));\n    }\n\n    @Override\n    protected void writeInternal(Properties properties, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {\n        // 获取请求头\n        HttpHeaders headers = outputMessage.getHeaders();\n        // 获取 content-type\n        MediaType contentType = headers.getContentType();\n        // 获取编码\n        Charset charset = null;\n        if (contentType != null) {\n            charset = contentType.getCharset();\n        }\n\n        charset = charset == null ? Charset.forName(\"UTF-8\") : charset;\n\n        // 获取请求体\n        OutputStream body = outputMessage.getBody();\n        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(body, charset);\n\n        properties.store(outputStreamWriter, \"Serialized by PropertiesHttpMessageConverter#writeInternal\");\n    }\n\n    @Override\n    protected Properties readInternal(Class<? extends Properties> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {\n        Properties properties = new Properties();\n        // 获取请求头\n        HttpHeaders headers = inputMessage.getHeaders();\n        // 获取 content-type\n        MediaType contentType = headers.getContentType();\n        // 获取编码\n        Charset charset = null;\n        if (contentType != null) {\n            charset = contentType.getCharset();\n        }\n\n        charset = charset == null ? Charset.forName(\"UTF-8\") : charset;\n\n        // 获取请求体\n        InputStream body = inputMessage.getBody();\n        InputStreamReader inputStreamReader = new InputStreamReader(body, charset);\n\n        properties.load(inputStreamReader);\n        return properties;\n    }\n\n    @Override\n    public Properties read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {\n        return readInternal(null, inputMessage);\n    }\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/handler/PropertiesHandlerMethodReturnValueHandler.java",
    "content": "package com.example.demo.handler;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServletServerHttpResponse;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.method.support.HandlerMethodReturnValueHandler;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.nio.charset.Charset;\nimport java.util.Properties;\n\n/**\n * @author MrBird\n */\npublic class PropertiesHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {\n\n    @Override\n    public boolean supportsReturnType(MethodParameter returnType) {\n        return Properties.class.equals(returnType.getMethod().getReturnType());\n    }\n\n    @Override\n    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {\n        Properties properties = (Properties) returnValue;\n\n        ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;\n\n        HttpServletResponse response = servletWebRequest.getResponse();\n        ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);\n\n        // 获取请求头\n        HttpHeaders headers = servletServerHttpResponse.getHeaders();\n\n        MediaType contentType = headers.getContentType();\n        // 获取编码\n        Charset charset = null;\n        if (contentType != null) {\n            charset = contentType.getCharset();\n        }\n\n        charset = charset == null ? Charset.forName(\"UTF-8\") : charset;\n\n        // 获取请求体\n        OutputStream body = servletServerHttpResponse.getBody();\n        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(body, charset);\n\n        properties.store(outputStreamWriter, \"Serialized by PropertiesHandlerMethodReturnValueHandler#handleReturnValue\");\n\n        // 告诉 Spring MVC 请求已经处理完毕\n        mavContainer.setRequestHandled(true);\n    }\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/java/com/example/demo/resolver/PropertiesHandlerMethodArgumentResolver.java",
    "content": "package com.example.demo.resolver;\n\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.support.WebDataBinderFactory;\nimport org.springframework.web.context.request.NativeWebRequest;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.method.support.HandlerMethodArgumentResolver;\nimport org.springframework.web.method.support.ModelAndViewContainer;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.nio.charset.Charset;\nimport java.util.Properties;\n\n/**\n * @author MrBird\n */\npublic class PropertiesHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {\n    @Override\n    public boolean supportsParameter(MethodParameter parameter) {\n        return Properties.class.equals(parameter.getParameterType());\n    }\n\n    @Override\n    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {\n        ServletWebRequest servletWebRequest = (ServletWebRequest) webRequest;\n        HttpServletRequest request = servletWebRequest.getRequest();\n        String contentType = request.getHeader(\"Content-Type\");\n\n        MediaType mediaType = MediaType.parseMediaType(contentType);\n        // 获取编码\n        Charset charset = mediaType.getCharset() == null ? Charset.forName(\"UTF-8\") : mediaType.getCharset();\n        // 获取输入流\n        InputStream inputStream = request.getInputStream();\n        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charset);\n\n        // 输入流转换为 Properties\n        Properties properties = new Properties();\n        properties.load(inputStreamReader);\n        return properties;\n    }\n}\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "47.Spring-Boot-Content-Negotiation/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-thymeleaf</artifactId>\n        </dependency>\n\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/main/java/com/example/demo/config/WebConfigurer.java",
    "content": "package com.example.demo.config;\n\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.config.annotation.CorsRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class WebConfigurer implements WebMvcConfigurer {\n\n    // @Override\n    // public void addCorsMappings(CorsRegistry registry) {\n    //     registry.addMapping(\"/**\")\n    //             .allowedOrigins(\"*\")\n    //             .allowedMethods(\"GET\");\n    // }\n\n    @Bean\n    public FilterRegistrationBean corsFilter() {\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowCredentials(true);\n        config.addAllowedOrigin(\"*\");\n        source.registerCorsConfiguration(\"/**\", config);\n        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));\n        bean.setOrder(0);\n        return bean;\n    }\n}\n"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.CrossOrigin;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n/**\n * @author MrBird\n */\n@Controller\npublic class TestController {\n\n    @RequestMapping(\"index\")\n    public String index() {\n        return \"index\";\n    }\n\n    @RequestMapping(\"hello\")\n    @ResponseBody\n    // @CrossOrigin(value = \"*\")\n    public String hello() {\n        return \"hello\";\n    }\n}\n"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>跨域测试</title>\n    <script src=\"http://libs.baidu.com/jquery/1.11.3/jquery.min.js\"></script>\n</head>\n<body>\n<div id=\"hello\"></div>\n</body>\n<script>\n    $(function () {\n        $.get(\"http://test.mrbird.cc:8080/hello\", function (data) {\n            $(\"#hello\").text(data);\n        })\n    })\n</script>\n</html>"
  },
  {
    "path": "48.Spring-Boot-CORS-Support/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "49.Spring-Boot-Async/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/main/java/com/example/demo/config/AsyncPoolConfig.java",
    "content": "package com.example.demo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class AsyncPoolConfig {\n\n    @Bean\n    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setCorePoolSize(20);\n        executor.setMaxPoolSize(200);\n        executor.setQueueCapacity(25);\n        executor.setKeepAliveSeconds(200);\n        executor.setThreadNamePrefix(\"asyncThread\");\n        executor.setWaitForTasksToCompleteOnShutdown(true);\n        executor.setAwaitTerminationSeconds(60);\n\n        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\n\n        executor.initialize();\n        return executor;\n    }\n}\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.service.TestService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class TestController {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private TestService testService;\n\n    @GetMapping(\"async\")\n    public String testAsync() throws Exception {\n        long start = System.currentTimeMillis();\n        logger.info(\"异步方法开始\");\n\n        Future<String> stringFuture = testService.asyncMethod();\n        String result = stringFuture.get(60, TimeUnit.SECONDS);\n        logger.info(\"异步方法返回值：{}\", result);\n\n        logger.info(\"异步方法结束\");\n\n        long end = System.currentTimeMillis();\n        logger.info(\"总耗时：{} ms\", end - start);\n        return stringFuture.get();\n    }\n\n    @GetMapping(\"sync\")\n    public void testSync() {\n        long start = System.currentTimeMillis();\n        logger.info(\"同步方法开始\");\n\n        testService.syncMethod();\n\n        logger.info(\"同步方法结束\");\n        long end = System.currentTimeMillis();\n        logger.info(\"总耗时：{} ms\", end - start);\n    }\n\n}\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/main/java/com/example/demo/service/TestService.java",
    "content": "package com.example.demo.service;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Service;\n\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * @author MrBird\n */\n@Service\npublic class TestService {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Async(\"asyncThreadPoolTaskExecutor\")\n    // @Async\n    public Future<String> asyncMethod() {\n        sleep();\n        logger.info(\"异步方法内部线程名称：{}\", Thread.currentThread().getName());\n        return new AsyncResult<>(\"hello async\");\n    }\n\n    public void syncMethod() {\n        sleep();\n    }\n\n    private void sleep() {\n        try {\n            TimeUnit.SECONDS.sleep(2);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "49.Spring-Boot-Async/src/test/java/com/example/demo/DemoApplicationTests.java",
    "content": "package com.example.demo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class DemoApplicationTests {\n\n    @Test\n    public void contextLoads() {\n    }\n\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/DemoApplication.java",
    "content": "package cc.mrbird;\n\nimport cc.mrbird.demo.config.WebConfig;\nimport cc.mrbird.demo.service.CalculateService;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.WebApplicationType;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\nimport java.util.Arrays;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        ConfigurableApplicationContext context1 = new SpringApplicationBuilder(DemoApplication.class)\n                .web(WebApplicationType.NONE)\n                .profiles(\"java7\")\n                .run(args);\n\n        // 返回 IOC 容器，使用注解配置，传入配置类\n        ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);\n        System.out.println(\"容器创建完毕\");\n\n        // User user = context.getBean(User.class);\n        // System.out.println(user);\n\n        // 查看 User 这个类在 Spring 容器中叫啥玩意\n        // String[] beanNames = context.getBeanNamesForType(User.class);\n        // Arrays.stream(beanNames).forEach(System.out::println);\n\n        // 查看基于注解的 IOC容器中所有组件名称\n        String[] beanNames = context.getBeanDefinitionNames();\n        Arrays.stream(beanNames).forEach(System.out::println);\n\n        // 组件的作用域\n        // Object user1 = context.getBean(\"user\");\n        // Object user2 = context.getBean(\"user\");\n        // System.out.println(user1 == user2);\n\n        // 测试懒加载\n        // Object user1 = context.getBean(\"user\");\n        // Object user2 = context.getBean(\"user\");\n\n        // 测试 Profile\n        CalculateService service = context1.getBean(CalculateService.class);\n        System.out.println(\"求合结果： \" + service.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));\n\n        // FactoryBean测试\n        Object cherry = context.getBean(\"cherryFactoryBean\");\n        System.out.println(cherry.getClass());\n\n        Object cherryFactoryBean = context.getBean(\"&cherryFactoryBean\");\n        System.out.println(cherryFactoryBean.getClass());\n\n    }\n}\n\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/condition/MyCondition.java",
    "content": "package cc.mrbird.demo.condition;\n\nimport org.springframework.context.annotation.Condition;\nimport org.springframework.context.annotation.ConditionContext;\nimport org.springframework.core.type.AnnotatedTypeMetadata;\n\n/**\n * @author MrBird\n */\npublic class MyCondition implements Condition {\n    @Override\n    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {\n        String osName = context.getEnvironment().getProperty(\"os.name\");\n        return osName != null && osName.contains(\"Windows\");\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/config/WebConfig.java",
    "content": "package cc.mrbird.demo.config;\n\nimport cc.mrbird.demo.condition.MyCondition;\nimport cc.mrbird.demo.domain.Hello;\nimport cc.mrbird.demo.domain.User;\nimport cc.mrbird.demo.factory.CherryFactoryBean;\nimport cc.mrbird.demo.filter.MyTypeFilter;\nimport cc.mrbird.demo.register.MyImportBeanDefinitionRegistrar;\nimport cc.mrbird.demo.selector.MyImportSelector;\nimport lombok.Builder;\nimport org.springframework.context.annotation.*;\nimport org.springframework.context.annotation.ComponentScan.Filter;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.stereotype.Repository;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author MrBird\n */\n@Configuration\n// @ComponentScan(value = \"cc.mrbird.demo\"\n        // , excludeFilters = {\n        //         @Filter(type = FilterType.ANNOTATION,\n        //                 classes = {Controller.class, Repository.class}),\n        //         @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = User.class)\n        //         @Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)\n        // }\n        // includeFilters = {\n        //         @Filter(type = FilterType.ANNOTATION, classes = Service.class)\n        // }, useDefaultFilters = false\n// )\n@Import({Hello.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})\npublic class WebConfig {\n\n    @Bean\n    // @Conditional(MyCondition.class)\n    // @Lazy\n    // @Scope(\"prototype\")\n    public User user() {\n        System.out.println(\"往IOC容器中注册user bean\");\n        return new User(\"mrbird\", 18);\n    }\n\n    @Bean\n    public CherryFactoryBean cherryFactoryBean() {\n        return new CherryFactoryBean();\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/controller/UserController.java",
    "content": "package cc.mrbird.demo.controller;\n\nimport org.springframework.stereotype.Controller;\n\n/**\n * @author MrBird\n */\n@Controller\npublic class UserController {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/dao/UserMapper.java",
    "content": "package cc.mrbird.demo.dao;\n\nimport org.springframework.stereotype.Repository;\n\n/**\n * @author MrBird\n */\n@Repository\npublic class UserMapper {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Apple.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Apple {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Banana.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Banana {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Cherry.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Cherry {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Hello.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Hello {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Strawberry.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Strawberry {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/User.java",
    "content": "package cc.mrbird.demo.domain;\n\nimport lombok.*;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@ToString\n@AllArgsConstructor\n@NoArgsConstructor\n@Data\n// @Component\npublic class User {\n    private String name;\n    private Integer age;\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/UserDetail.java",
    "content": "package cc.mrbird.demo.domain;\n\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n// @Component\npublic class UserDetail extends User {\n\n    private int sex;\n\n\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/domain/Watermelon.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class Watermelon {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/factory/CherryFactoryBean.java",
    "content": "package cc.mrbird.demo.factory;\n\nimport cc.mrbird.demo.domain.Cherry;\nimport org.springframework.beans.factory.FactoryBean;\n\n/**\n * @author MrBird\n */\npublic class CherryFactoryBean implements FactoryBean<Cherry> {\n    @Override\n    public Cherry getObject() {\n        return new Cherry();\n    }\n\n    @Override\n    public Class<?> getObjectType() {\n        return Cherry.class;\n    }\n\n    @Override\n    public boolean isSingleton() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/filter/MyTypeFilter.java",
    "content": "package cc.mrbird.demo.filter;\n\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.type.AnnotationMetadata;\nimport org.springframework.core.type.ClassMetadata;\nimport org.springframework.core.type.classreading.MetadataReader;\nimport org.springframework.core.type.classreading.MetadataReaderFactory;\nimport org.springframework.core.type.filter.TypeFilter;\nimport org.springframework.util.StringUtils;\n\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\npublic class MyTypeFilter implements TypeFilter {\n    @Override\n    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {\n        // 获取当前正在扫描的类的注解信息\n        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();\n        // 获取当前正在扫描的类的类信息\n        ClassMetadata classMetadata = metadataReader.getClassMetadata();\n        // 获取当前正在扫描的类的路径等信息\n        Resource resource = metadataReader.getResource();\n\n        String className = classMetadata.getClassName();\n        return StringUtils.hasText(\"er\");\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/register/MyImportBeanDefinitionRegistrar.java",
    "content": "package cc.mrbird.demo.register;\n\nimport cc.mrbird.demo.domain.Strawberry;\nimport org.springframework.beans.factory.support.BeanDefinitionRegistry;\nimport org.springframework.beans.factory.support.RootBeanDefinition;\nimport org.springframework.context.annotation.ImportBeanDefinitionRegistrar;\nimport org.springframework.core.type.AnnotationMetadata;\n\n/**\n * @author MrBird\n */\npublic class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {\n    @Override\n    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {\n        final String beanName = \"strawberry\";\n        boolean contain = registry.containsBeanDefinition(beanName);\n        if (!contain) {\n            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Strawberry.class);\n            registry.registerBeanDefinition(beanName, rootBeanDefinition);\n        }\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/selector/MyImportSelector.java",
    "content": "package cc.mrbird.demo.selector;\n\nimport org.springframework.context.annotation.ImportSelector;\nimport org.springframework.core.type.AnnotationMetadata;\n\n/**\n * @author MrBird\n */\npublic class MyImportSelector implements ImportSelector {\n\n    @Override\n    public String[] selectImports(AnnotationMetadata importingClassMetadata) {\n        return new String[]{\n                \"cc.mrbird.demo.domain.Apple\",\n                \"cc.mrbird.demo.domain.Banana\",\n                \"cc.mrbird.demo.domain.Watermelon\"\n        };\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/service/CalculateService.java",
    "content": "package cc.mrbird.demo.service;\n\n/**\n * @author MrBird\n */\npublic interface CalculateService {\n\n    Integer sum(Integer... value);\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/service/UserService.java",
    "content": "package cc.mrbird.demo.service;\n\nimport org.springframework.stereotype.Service;\n\n/**\n * @author MrBird\n */\n@Service\npublic class UserService {\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/service/impl/Java7CalculateServiceImpl.java",
    "content": "package cc.mrbird.demo.service.impl;\n\nimport cc.mrbird.demo.service.CalculateService;\nimport org.springframework.context.annotation.Profile;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author MrBird\n */\n@Service\n@Profile(\"java7\")\npublic class Java7CalculateServiceImpl implements CalculateService {\n\n    @Override\n    public Integer sum(Integer... value) {\n        System.out.println(\"Java 7环境下执行\");\n        int result = 0;\n        for (int i = 0; i <= value.length; i++) {\n            result += i;\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/java/cc/mrbird/demo/service/impl/Java8CalculateServiceImpl.java",
    "content": "package cc.mrbird.demo.service.impl;\n\nimport cc.mrbird.demo.service.CalculateService;\nimport org.springframework.context.annotation.Profile;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\n\n/**\n * @author MrBird\n */\n@Service\n@Profile(\"java8\")\npublic class Java8CalculateServiceImpl implements CalculateService {\n\n    @Override\n    public Integer sum(Integer... value) {\n        System.out.println(\"Java 8环境下执行\");\n        return Arrays.stream(value).reduce(0, Integer::sum);\n    }\n}\n"
  },
  {
    "path": "50.Spring-Regist-Bean/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "51.Spring-Bean-Lifecycle/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/DemoApplication.java",
    "content": "package cc.mrbird;\n\nimport cc.mrbird.demo.config.WebConfig;\nimport cc.mrbird.demo.domain.User;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n\n        // 返回 IOC 容器，使用注解配置，传入配置类\n        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);\n        System.out.println(\"容器创建完毕\");\n        // User user = context.getBean(User.class);\n        // 关闭 IOC 容器\n        context.close();\n    }\n}\n\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/demo/config/MyBeanPostProcessor.java",
    "content": "package cc.mrbird.demo.config;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.lang.Nullable;\n\n/**\n * @author MrBird\n */\npublic class MyBeanPostProcessor implements BeanPostProcessor {\n\n    @Override\n    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n        System.out.println(beanName + \" 初始化之前调用\");\n        return bean;\n    }\n\n    @Override\n    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n        System.out.println(beanName + \" 初始化之后调用\");\n        return bean;\n    }\n}\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/demo/config/WebConfig.java",
    "content": "package cc.mrbird.demo.config;\n\nimport cc.mrbird.demo.domain.Bird;\nimport cc.mrbird.demo.domain.Fish;\nimport cc.mrbird.demo.domain.User;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Scope;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class WebConfig {\n\n    // @Scope(\"prototype\")\n    // @Bean(initMethod = \"init\", destroyMethod = \"destory\")\n    // public User user() {\n    //     return new User();\n    // }\n\n    // @Bean\n    // public Bird bird() {\n    //     return new Bird();\n    // }\n\n    @Bean\n    public Fish fish(){\n        return new Fish();\n    }\n\n    @Bean\n    public MyBeanPostProcessor myBeanPostProcessor () {\n        return new MyBeanPostProcessor();\n    }\n}\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/demo/domain/Bird.java",
    "content": "package cc.mrbird.demo.domain;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * @author MrBird\n */\npublic class Bird implements InitializingBean, DisposableBean {\n\n    public Bird() {\n        System.out.println(\"调用无参构造器创建Bird\");\n    }\n\n    @Override\n    public void destroy() {\n        System.out.println(\"销毁Bird\");\n    }\n\n    @Override\n    public void afterPropertiesSet() {\n        System.out.println(\"初始化Bird\");\n    }\n}\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/demo/domain/Fish.java",
    "content": "package cc.mrbird.demo.domain;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\n\n/**\n * @author MrBird\n */\npublic class Fish {\n\n    public Fish() {\n        System.out.println(\"调用无参构造器创建Fish\");\n    }\n\n    @PostConstruct\n    public void init() {\n        System.out.println(\"初始化Fish\");\n    }\n\n    @PreDestroy\n    public void destory() {\n        System.out.println(\"销毁Fish\");\n    }\n}\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/java/cc/mrbird/demo/domain/User.java",
    "content": "package cc.mrbird.demo.domain;\n\n/**\n * @author MrBird\n */\npublic class User {\n\n    public User() {\n        System.out.println(\"调用无参构造器创建User\");\n    }\n\n    public void init() {\n        System.out.println(\"初始化User\");\n    }\n\n    public void destory() {\n        System.out.println(\"销毁User\");\n    }\n}\n"
  },
  {
    "path": "51.Spring-Bean-Lifecycle/src/main/resources/application.properties",
    "content": ""
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/common-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>common-api</artifactId>\n\n</project>"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/common-api/src/main/java/cc/mrbird/common/api/HelloService.java",
    "content": "package cc.mrbird.common.api;\n\npublic interface HelloService {\n    String hello(String message);\n}\n"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>dubbo-boot</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <name>dubbo-boot</name>\n    <description>Spring Boot-Dubbo-ZooKeeper</description>\n\n    <modules>\n        <module>common-api</module>\n        <module>server-provider</module>\n        <module>server-consumer</module>\n    </modules>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <project.version>1.0</project.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- dubbo -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>0.2.0</version>\n        </dependency>\n\n        <!-- zookeeper -->\n        <dependency>\n            <groupId>org.apache.zookeeper</groupId>\n            <artifactId>zookeeper</artifactId>\n            <version>3.4.8</version>\n        </dependency>\n        <dependency>\n            <groupId>com.101tec</groupId>\n            <artifactId>zkclient</artifactId>\n            <version>0.10</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-consumer</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-consumer/src/main/java/cc/mrbird/ConsumerApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@EnableDubbo\n@SpringBootApplication\npublic class ConsumerApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplicaiton.class, args);\n    }\n}\n"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-consumer/src/main/java/cc/mrbird/consumer/controller/HelloController.java",
    "content": "package cc.mrbird.consumer.controller;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HelloController {\n\n    @Reference\n    private HelloService helloService;\n\n    @GetMapping(\"/hello/{message}\")\n    public String hello(@PathVariable String message) {\n        return this.helloService.hello(message);\n    }\n}\n"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-consumer\n    # zookeeper地址，用于从中获取注册的服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo\n  monitor:\n    protocol: registry"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-provider</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-provider/src/main/java/cc/mrbird/ProviderApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@EnableDubbo\n@SpringBootApplication\npublic class ProviderApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplicaiton.class, args);\n        System.out.println(\"complete\");\n    }\n}\n"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-provider/src/main/java/cc/mrbird/provider/service/HelloServiceImpl.java",
    "content": "package cc.mrbird.provider.service;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Service;\nimport org.springframework.stereotype.Component;\n\n@Service(interfaceClass = HelloService.class)\n@Component\npublic class HelloServiceImpl implements HelloService {\n    @Override\n    public String hello(String message) {\n        return \"hello,\" + message;\n    }\n}\n"
  },
  {
    "path": "52.Dubbo-OPS-Mointor/spring-boot-dubbo-applicaiton/server-provider/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-provider\n    # zookeeper地址，用于向其注册服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  #暴露服务方式\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo\n    # 暴露服务端口 （默认是20880，不同的服务提供者端口不能重复）\n    port: 20880\n  monitor:\n    protocol: registry"
  },
  {
    "path": "53.Dubbo-High-Availability/common-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>common-api</artifactId>\n\n</project>"
  },
  {
    "path": "53.Dubbo-High-Availability/common-api/src/main/java/cc/mrbird/common/api/HelloService.java",
    "content": "package cc.mrbird.common.api;\n\npublic interface HelloService {\n    String hello(String message);\n}\n"
  },
  {
    "path": "53.Dubbo-High-Availability/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>dubbo-boot</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0</version>\n\n    <name>dubbo-boot</name>\n    <description>Spring Boot-Dubbo-ZooKeeper</description>\n\n    <modules>\n        <module>common-api</module>\n        <module>server-provider</module>\n        <module>server-consumer</module>\n    </modules>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.4.RELEASE</version>\n        <relativePath/>\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <project.version>1.0</project.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- dubbo -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>0.2.0</version>\n        </dependency>\n\n        <!-- zookeeper -->\n        <dependency>\n            <groupId>org.apache.zookeeper</groupId>\n            <artifactId>zookeeper</artifactId>\n            <version>3.4.8</version>\n        </dependency>\n        <dependency>\n            <groupId>com.101tec</groupId>\n            <artifactId>zkclient</artifactId>\n            <version>0.10</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "53.Dubbo-High-Availability/server-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-consumer</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "53.Dubbo-High-Availability/server-consumer/src/main/java/cc/mrbird/ConsumerApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@EnableDubbo\n@SpringBootApplication\npublic class ConsumerApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplicaiton.class, args);\n    }\n}\n"
  },
  {
    "path": "53.Dubbo-High-Availability/server-consumer/src/main/java/cc/mrbird/consumer/controller/HelloController.java",
    "content": "package cc.mrbird.consumer.controller;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class HelloController {\n\n    // @Reference(url = \"http://127.0.0.1:8080\")\n    // @Reference(loadbalance = RoundRobinLoadBalance.NAME)\n    @Reference(timeout = 1000)\n    private HelloService helloService;\n\n    @GetMapping(\"/hello/{message}\")\n    public String hello(@PathVariable String message) {\n        return this.helloService.hello(message);\n    }\n\n}\n"
  },
  {
    "path": "53.Dubbo-High-Availability/server-consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-consumer\n    # zookeeper地址，用于从中获取注册的服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo\n  monitor:\n    protocol: registry"
  },
  {
    "path": "53.Dubbo-High-Availability/server-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>dubbo-boot</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>1.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>server-provider</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cc.mrbird</groupId>\n            <artifactId>common-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n            <version>2.0.2.RELEASE</version>\n        </dependency>\n    </dependencies>\n</project>"
  },
  {
    "path": "53.Dubbo-High-Availability/server-provider/src/main/java/cc/mrbird/ProviderApplicaiton.java",
    "content": "package cc.mrbird;\n\nimport com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.hystrix.EnableHystrix;\n\n@EnableHystrix\n@EnableDubbo\n@SpringBootApplication\npublic class ProviderApplicaiton {\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplicaiton.class, args);\n        System.out.println(\"complete\");\n    }\n}\n"
  },
  {
    "path": "53.Dubbo-High-Availability/server-provider/src/main/java/cc/mrbird/provider/service/HelloServiceImpl.java",
    "content": "package cc.mrbird.provider.service;\n\nimport cc.mrbird.common.api.HelloService;\nimport com.alibaba.dubbo.config.annotation.Service;\nimport com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.TimeUnit;\n\n@Service(\n        interfaceClass = HelloService.class,\n        weight = 100,\n        loadbalance = RoundRobinLoadBalance.NAME)\n@Component\npublic class HelloServiceImpl implements HelloService {\n\n    @Override\n    @HystrixCommand(fallbackMethod = \"defaultHello\")\n    public String hello(String message) {\n        System.out.println(\"调用 cc.mrbird.provider.service.HelloServiceImpl#hello\");\n        // try {\n        //     TimeUnit.SECONDS.sleep(2);\n        // } catch (InterruptedException e) {\n        //     e.printStackTrace();\n        // }\n        String a = null;\n        a.toString();\n        return \"hello,\" + message;\n    }\n\n    public String defaultHello(String message) {\n        return \"hello anonymous\";\n    }\n}\n"
  },
  {
    "path": "53.Dubbo-High-Availability/server-provider/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\n\ndubbo:\n  application:\n    # 服务名称，保持唯一\n    name: server-provider\n    # zookeeper地址，用于向其注册服务\n  registry:\n    address: zookeeper://127.0.0.1:2181\n  #暴露服务方式\n  protocol:\n    # dubbo协议，固定写法\n    name: dubbo\n    # 暴露服务端口 （默认是20880，不同的服务提供者端口不能重复）\n    port: 20880\n  monitor:\n    protocol: registry\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>demo</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/KafkaApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class KafkaApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(KafkaApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/config/KafkaConsumerConfig.java",
    "content": "package com.example.demo.config;\n\nimport com.example.demo.domain.Message;\nimport org.apache.kafka.clients.consumer.ConsumerConfig;\nimport org.apache.kafka.common.serialization.StringDeserializer;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.kafka.annotation.EnableKafka;\nimport org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;\nimport org.springframework.kafka.core.ConsumerFactory;\nimport org.springframework.kafka.core.DefaultKafkaConsumerFactory;\nimport org.springframework.kafka.support.serializer.JsonDeserializer;\nimport org.springframework.kafka.support.serializer.JsonSerializer;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@EnableKafka\n@Configuration\npublic class KafkaConsumerConfig {\n\n    @Value(\"${spring.kafka.bootstrap-servers}\")\n    private String bootstrapServers;\n\n    @Value(\"${spring.kafka.consumer.group-id}\")\n    private String consumerGroupId;\n\n    @Value(\"${spring.kafka.consumer.auto-offset-reset}\")\n    private String autoOffsetReset;\n\n    @Bean\n    public ConsumerFactory<String, Message> consumerFactory() {\n        Map<String, Object> props = new HashMap<>();\n        props.put(\n                ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,\n                bootstrapServers);\n        props.put(\n                ConsumerConfig.GROUP_ID_CONFIG,\n                consumerGroupId);\n        props.put(\n                ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,\n                autoOffsetReset);\n        // props.put(\n        //         ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,\n        //         StringDeserializer.class);\n        // props.put(\n        //         ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,\n        //         StringDeserializer.class);\n        return new DefaultKafkaConsumerFactory<>(\n                props,\n                new StringDeserializer(),\n                new JsonDeserializer<>(Message.class));\n    }\n\n    @Bean\n    public ConcurrentKafkaListenerContainerFactory<String, Message> kafkaListenerContainerFactory() {\n        ConcurrentKafkaListenerContainerFactory<String, Message> factory\n                = new ConcurrentKafkaListenerContainerFactory<>();\n        factory.setConsumerFactory(consumerFactory());\n        // factory.setRecordFilterStrategy(\n        //         r -> r.value().contains(\"fuck\")\n        // );\n        return factory;\n    }\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/config/KafkaProducerConfig.java",
    "content": "package com.example.demo.config;\n\nimport com.example.demo.domain.Message;\nimport org.apache.kafka.clients.producer.ProducerConfig;\nimport org.apache.kafka.common.serialization.StringSerializer;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.kafka.core.DefaultKafkaProducerFactory;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.core.ProducerFactory;\nimport org.springframework.kafka.support.serializer.JsonSerializer;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class KafkaProducerConfig {\n\n    @Value(\"${spring.kafka.bootstrap-servers}\")\n    private String bootstrapServers;\n\n    @Bean\n    public ProducerFactory<String, Message> producerFactory() {\n        Map<String, Object> configProps = new HashMap<>();\n        configProps.put(\n                ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,\n                bootstrapServers);\n        configProps.put(\n                ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,\n                StringSerializer.class);\n        configProps.put(\n                ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,\n                JsonSerializer.class);\n        return new DefaultKafkaProducerFactory<>(configProps);\n    }\n\n    @Bean\n    public KafkaTemplate<String, Message> kafkaTemplate() {\n        return new KafkaTemplate<>(producerFactory());\n    }\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/controller/SendMessageController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.domain.Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.util.concurrent.ListenableFuture;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class SendMessageController {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    // private KafkaTemplate<String, String> kafkaTemplate;\n    private KafkaTemplate<String, Message> kafkaTemplate;\n\n    // @GetMapping(\"send/{message}\")\n    // public void send(@PathVariable String message) {\n        // this.kafkaTemplate.send(\"test\", message);\n        // ListenableFuture<SendResult<String, String>> future = this.kafkaTemplate.send(\"test\", message);\n        // future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {\n        //     @Override\n        //     public void onSuccess(SendResult<String, String> result) {\n        //         logger.info(\"成功发送消息：{}，offset=[{}]\", message, result.getRecordMetadata().offset());\n        //     }\n        //\n        //     @Override\n        //     public void onFailure(Throwable ex) {\n        //         logger.error(\"消息：{} 发送失败，原因：{}\", message, ex.getMessage());\n        //     }\n        // });\n    // }\n\n    @GetMapping(\"send/{message}\")\n    public void sendMessage(@PathVariable String message) {\n        this.kafkaTemplate.send(\"test\", new Message(\"mrbird\", message));\n    }\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/domain/Message.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\nimport java.time.LocalTime;\n\n/**\n * @author MrBird\n */\npublic class Message implements Serializable {\n    private static final long serialVersionUID = 6678420965611108427L;\n\n    private String from;\n\n    private String message;\n\n    public Message() {\n    }\n\n    public Message(String from, String message) {\n        this.from = from;\n        this.message = message;\n    }\n\n    @Override\n    public String toString() {\n        return \"Message{\" +\n                \"from='\" + from + '\\'' +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n\n    public String getFrom() {\n        return from;\n    }\n\n    public void setFrom(String from) {\n        this.from = from;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/java/com/example/demo/listener/KafkaMessageListener.java",
    "content": "package com.example.demo.listener;\n\nimport com.example.demo.domain.Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.kafka.annotation.PartitionOffset;\nimport org.springframework.kafka.annotation.TopicPartition;\nimport org.springframework.kafka.support.KafkaHeaders;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class KafkaMessageListener {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    // @KafkaListener(topics = \"test\", groupId = \"test-consumer\")\n    // @KafkaListener(groupId = \"test-consumer\",\n    //         topicPartitions = @TopicPartition(topic = \"test\",\n    //                 partitionOffsets = {\n    //                         @PartitionOffset(partition = \"0\", initialOffset = \"0\")\n    //                 }))\n    // public void listen(@Payload String message,\n    //                    @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {\n    //     logger.info(\"接收消息: {}，partition：{}\", message, partition);\n    // }\n\n    @KafkaListener(topics = \"test\", groupId = \"test-consumer\")\n    public void listen(Message message) {\n        logger.info(\"接收消息: {}\", message);\n    }\n\n}\n"
  },
  {
    "path": "54.Spring-Boot-Kafka/src/main/resources/application.yml",
    "content": "spring:\n  kafka:\n    bootstrap-servers: localhost:9092\n    consumer:\n      group-id: test-consumer\n      auto-offset-reset: latest\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>server-consumer</artifactId>\n    <version>1.0</version>\n    <name>server-consumer</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-consumer/src/main/java/com/example/demo/ServerConsumerApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ServerConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ServerConsumerApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-consumer/src/main/java/com/example/demo/TestController.java",
    "content": "package com.example.demo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.net.URI;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class TestController {\n\n    private Logger loggr = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private DiscoveryClient discoveryClient;\n    @Autowired\n    private LoadBalancerClient loadBalancerClient;\n\n    private final RestTemplate restTemplate = new RestTemplate();\n\n    // @Autowired\n    // private RestTemplate restTemplate;\n\n    private static final String SERVER_ID = \"server-provider\";\n\n    @GetMapping(\"uri\")\n    public List<URI> getServerUris() {\n        return this.discoveryClient.getInstances(SERVER_ID)\n                .stream()\n                .map(ServiceInstance::getUri).collect(Collectors.toList());\n    }\n\n    @GetMapping(\"hello\")\n    public String hello() {\n        ServiceInstance instance = loadBalancerClient.choose(SERVER_ID);\n        String url = instance.getUri().toString() + \"/hello\";\n        loggr.info(\"remote server url：{}\", url);\n        return restTemplate.getForObject(url, String.class);\n    }\n\n    // @Bean\n    // @LoadBalanced\n    // public RestTemplate restTemplate() {\n    //     return new RestTemplate();\n    // }\n\n    // @GetMapping(\"hello\")\n    // public String hello() {\n    //     String url = \"http://\" + SERVER_ID + \"/hello\";\n    //     return restTemplate.getForObject(url, String.class);\n    // }\n\n}\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9002\n\nspring:\n  application:\n    name: server-consumer\n\n  cloud:\n    consul:\n      host: 192.168.140.215\n      port: 8500\n      discovery:\n        service-name: ${spring.application.name}"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-proivder/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.0.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>server-provider</artifactId>\n    <version>1.0</version>\n    <name>server-provider</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-proivder/src/main/java/com/example/demo/ServerProviderApplication.java",
    "content": "package com.example.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@EnableDiscoveryClient\n@SpringBootApplication\npublic class ServerProviderApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(ServerProviderApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-proivder/src/main/java/com/example/demo/TestController.java",
    "content": "package com.example.demo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class TestController {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"check\")\n    private String check() {\n        logger.info(\"health check\");\n        return \"ok\";\n    }\n\n    @GetMapping(\"hello\")\n    public String hello() {\n        logger.info(\"hello\");\n        return \"hello from server provider\";\n    }\n}\n"
  },
  {
    "path": "55.Spring-Cloud-Consul/server-proivder/src/main/resources/application.yml",
    "content": "server:\n  port: 9000\nspring:\n  application:\n    name: server-provider\n\n  cloud:\n    consul:\n      host: 192.168.140.215\n      port: 8500\n      discovery:\n        health-check-interval: 10s\n        service-name: ${spring.application.name}\n        register-health-check: true\n        health-check-path: /check\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/Mongo DB crud.postman_collection.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"8fa1e028-3088-4d19-bb08-4d41b3802487\",\n\t\t\"name\": \"Mongo DB crud\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"createUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user?name=mike&description=python developer&age=21\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"name\",\n\t\t\t\t\t\t\t\"value\": \"mike\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"python developer\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"age\",\n\t\t\t\t\t\t\t\"value\": \"21\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUsers\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca56280f08f0b6048fd470b\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"updateUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"PUT\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca40ee8f08f0b68cc06c001?id=5ca56280f08f0b6048fd470b&description=Java gosu&age=19\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca40ee8f08f0b68cc06c001\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"id\",\n\t\t\t\t\t\t\t\"value\": \"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"Java gosu\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"age\",\n\t\t\t\t\t\t\t\"value\": \"19\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"deleteUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"DELETE\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca56280f08f0b6048fd470b\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUsersStream\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/stream\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"stream\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByAge\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/age/20/30\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"age\",\n\t\t\t\t\t\t\"20\",\n\t\t\t\t\t\t\"30\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByName\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/name/mrbird\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"mrbird\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByDescription\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/description/ui\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"description\",\n\t\t\t\t\t\t\"ui\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByCondition\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/condition?size=2&page=0\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"condition\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"name\",\n\t\t\t\t\t\t\t\"value\": \"mrbird\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"r\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"size\",\n\t\t\t\t\t\t\t\"value\": \"2\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"page\",\n\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t]\n}"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>mongodb</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>mongodb</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/java/com/example/mongodb/MongodbApplication.java",
    "content": "package com.example.mongodb;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MongodbApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MongodbApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/java/com/example/mongodb/controller/UserController.java",
    "content": "package com.example.mongodb.controller;\n\nimport com.example.mongodb.domain.User;\nimport com.example.mongodb.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.domain.Page;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping\n    public List<User> getUsers() {\n        return userService.getUsers();\n    }\n\n    @PostMapping\n    public User createUser(User user) {\n        return userService.createUser(user);\n    }\n\n    @DeleteMapping(\"/{id}\")\n    public void deleteUser(@PathVariable String id) {\n        userService.deleteUser(id);\n    }\n\n    @PutMapping(\"/{id}\")\n    public void updateUser(@PathVariable String id, User user) {\n        userService.updateUser(id, user);\n    }\n\n    /**\n     * 根据用户 id查找\n     * 存在返回，不存在返回 null\n     */\n    @GetMapping(\"/{id}\")\n    public User getUser(@PathVariable String id) {\n        return userService.getUser(id).orElse(null);\n    }\n\n    /**\n     * 根据年龄段来查找\n     */\n    @GetMapping(\"/age/{from}/{to}\")\n    public List<User> getUserByAge(@PathVariable Integer from, @PathVariable Integer to) {\n        return userService.getUserByAge(from, to);\n    }\n\n    /**\n     * 根据用户名查找\n     */\n    @GetMapping(\"/name/{name}\")\n    public List<User> getUserByName(@PathVariable String name) {\n        return userService.getUserByName(name);\n    }\n\n    /**\n     * 根据用户描述模糊查找\n     */\n    @GetMapping(\"/description/{description}\")\n    public List<User> getUserByDescription(@PathVariable String description) {\n        return userService.getUserByDescription(description);\n    }\n\n    /**\n     * 根据多个检索条件查询\n     */\n    @GetMapping(\"/condition\")\n    public Page<User> getUserByCondition(int size, int page, User user) {\n        return userService.getUserByCondition(size, page, user);\n    }\n\n}\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/java/com/example/mongodb/dao/UserDao.java",
    "content": "package com.example.mongodb.dao;\n\nimport com.example.mongodb.domain.User;\nimport org.springframework.data.mongodb.repository.MongoRepository;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Repository\npublic interface UserDao extends MongoRepository<User, String> {\n\n    /**\n     * 根据年龄段来查找\n     *\n     * @param from from\n     * @param to   to\n     * @return List<User>\n     */\n    List<User> findByAgeBetween(Integer from, Integer to);\n\n\n    /**\n     * 通过年龄段，用户名，描述（模糊查询）\n     *\n     * @param from        from\n     * @param to          to\n     * @param name        name\n     * @param description description\n     * @return List<User>\n     */\n    List<User> findByAgeBetweenAndNameEqualsAndDescriptionIsLike(Integer from, Integer to, String name, String description);\n\n    /**\n     * 更具描述来模糊查询用户\n     *\n     * @param description 描述\n     * @return List<User>\n     */\n    List<User> findByDescriptionIsLike(String description);\n\n    /**\n     * 通过用户名查询\n     *\n     * @param name 用户名\n     * @return List<User>\n     */\n    List<User> findByNameEquals(String name);\n\n}\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/java/com/example/mongodb/domain/User.java",
    "content": "package com.example.mongodb.domain;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.annotation.Transient;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\n/**\n * @author MrBird\n */\n@Document(collection = \"user\")\npublic class User {\n\n    @Id\n    private String id;\n\n    private String name;\n\n    private Integer age;\n\n    private String description;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n}\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/java/com/example/mongodb/service/UserService.java",
    "content": "package com.example.mongodb.service;\n\nimport com.example.mongodb.dao.UserDao;\nimport com.example.mongodb.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.data.repository.support.PageableExecutionUtils;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StringUtils;\n\nimport java.util.List;\nimport java.util.Optional;\n\n/**\n * @author MrBird\n */\n@Service\npublic class UserService {\n\n    @Autowired\n    private UserDao userDao;\n    @Autowired\n    private MongoTemplate template;\n\n    public List<User> getUsers() {\n        return userDao.findAll();\n    }\n\n    public Optional<User> getUser(String id) {\n        return this.userDao.findById(id);\n    }\n\n    /**\n     * 新增和修改都是 save方法，\n     * id 存在为修改，id 不存在为新增\n     */\n    public User createUser(User user) {\n        user.setId(null);\n        return userDao.save(user);\n    }\n\n    public void deleteUser(String id) {\n        this.userDao.findById(id)\n                .ifPresent(user -> this.userDao.delete(user));\n    }\n\n    public void updateUser(String id, User user) {\n        this.userDao.findById(id)\n                .ifPresent(\n                        u -> {\n                            u.setName(user.getName());\n                            u.setAge(user.getAge());\n                            u.setDescription(user.getDescription());\n                            this.userDao.save(u);\n                        }\n                );\n    }\n\n    public List<User> getUserByAge(Integer from, Integer to) {\n        return this.userDao.findByAgeBetween(from, to);\n    }\n\n    public List<User> getUserByName(String name) {\n        return this.userDao.findByNameEquals(name);\n    }\n\n    public List<User> getUserByDescription(String description) {\n        return this.userDao.findByDescriptionIsLike(description);\n    }\n\n    public Page<User> getUserByCondition(int size, int page, User user) {\n        Query query = new Query();\n        Criteria criteria = new Criteria();\n\n        if (!StringUtils.isEmpty(user.getName())) {\n            criteria.and(\"name\").is(user.getName());\n        }\n        if (!StringUtils.isEmpty(user.getDescription())) {\n            criteria.and(\"description\").regex(user.getDescription());\n        }\n\n        query.addCriteria(criteria);\n\n        Sort sort = new Sort(Sort.Direction.DESC, \"age\");\n        Pageable pageable = PageRequest.of(page, size, sort);\n\n        List<User> users = template.find(query.with(pageable), User.class);\n        return PageableExecutionUtils.getPage(users, pageable, () -> template.count(query, User.class));\n    }\n}\n"
  },
  {
    "path": "56.Spring-Boot-MongoDB-crud/src/main/resources/application.yml",
    "content": "spring:\n  data:\n    mongodb:\n      host: localhost\n      port: 27017\n      database: testdb"
  },
  {
    "path": "57.Spring-Boot-WebFlux/async-servlet/src/AsyncServlet.java",
    "content": "import javax.servlet.AsyncContext;\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\n\n/**\n * @author MrBird\n */\n@WebServlet(urlPatterns = \"/async\", asyncSupported = true)\npublic class AsyncServlet extends HttpServlet {\n    private static final long serialVersionUID = 393375716683413545L;\n\n    private Logger log = Logger.getLogger(AsyncServlet.class.getName());\n\n    @Override\n    protected void doGet(HttpServletRequest request, HttpServletResponse response) {\n        long start = System.currentTimeMillis();\n        AsyncContext asyncContext = request.startAsync();\n\n        CompletableFuture.runAsync(() -> execute(asyncContext, asyncContext.getRequest(), asyncContext.getResponse()));\n        log.info(\"总耗时：\" + (System.currentTimeMillis() - start) + \"ms\");\n    }\n\n    private void execute(AsyncContext asyncContext, ServletRequest request, ServletResponse response) {\n        try {\n            TimeUnit.SECONDS.sleep(2);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        try {\n            response.getWriter().append(\"hello\");\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        asyncContext.complete();\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/async-servlet/src/SyncServlet.java",
    "content": "import javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\n\n/**\n * @author MrBird\n */\n@WebServlet(urlPatterns = \"/sync\")\npublic class SyncServlet extends HttpServlet {\n\n    private static final long serialVersionUID = 7583536145022393360L;\n\n    private Logger log = Logger.getLogger(SyncServlet.class.getName());\n\n    @Override\n    protected void doGet(HttpServletRequest request, HttpServletResponse response) {\n\n        long start = System.currentTimeMillis();\n        this.execute(request, response);\n        log.info(\"总耗时：\" + (System.currentTimeMillis() - start) + \"ms\");\n    }\n\n    private void execute(HttpServletRequest request, HttpServletResponse response) {\n        try {\n            TimeUnit.SECONDS.sleep(2);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        try {\n            response.getWriter().append(\"hello\");\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/async-servlet/web/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd\"\n         version=\"4.0\">\n</web-app>"
  },
  {
    "path": "57.Spring-Boot-WebFlux/async-servlet/web/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>hello</title>\n</head>\n<body>\n\n</body>\n</html>"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>webflux</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>webflux</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-thymeleaf</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/FluxTest.java",
    "content": "package com.example.webflux;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\nimport java.util.ArrayList;\nimport java.util.Random;\n\n/**\n * @author MrBird\n */\npublic class FluxTest {\n\n    public static void main(String[] args) throws InterruptedException {\n        Flux.just(\"Hello\", \"World\").subscribe(System.out::println);\n        Flux.fromArray(new Integer[]{1, 2, 3}).subscribe(System.out::println);\n        Flux.empty().subscribe(System.out::println);\n        Flux.range(1, 4).subscribe(System.out::println);\n        // Flux.interval(Duration.of(1, ChronoUnit.SECONDS)).subscribe(System.out::println);\n\n        Flux.generate(sink -> {\n            sink.next(\"Hello\");\n            sink.complete();\n        }).subscribe(System.out::println);\n\n\n        final Random random = new Random();\n        Flux.generate(ArrayList::new, (list, sink) -> {\n            int value = random.nextInt(100);\n            list.add(value);\n            sink.next(value);\n            if (list.size() == 10) {\n                sink.complete();\n            }\n            return list;\n        }).subscribe(System.out::println);\n\n        Flux.create(sink -> {\n            for (int i = 0; i < 10; i++) {\n                sink.next(i);\n            }\n            sink.complete();\n        }).subscribe(System.out::println);\n\n        Flux.range(1, 10).filter(i -> i % 2 == 0).subscribe(System.out::println);\n\n        Flux.range(1, 20).take(10).subscribe(System.out::println);\n        Flux.range(1, 20).takeLast(10).subscribe(System.out::println);\n        Flux.range(1, 20).takeWhile(i -> i < 10).subscribe(System.out::println);\n        Flux.range(1, 20).takeUntil(i -> i == 10).subscribe(System.out::println);\n\n        Flux.range(1, 10).reduce((x, y) -> x + y).subscribe(System.out::println);\n        Flux.range(1, 10).reduceWith(() -> 10, (x, y) -> x + y).subscribe(System.out::println);\n\n        Flux.merge(\n                Flux.interval(Duration.of(500, ChronoUnit.MILLIS)).take(2),\n                Flux.interval(Duration.of(500, ChronoUnit.MILLIS)).take(2)\n        ).toStream().forEach(System.out::println);\n\n        Flux.range(1, 100).buffer(20).subscribe(System.out::println);\n\n        Flux.range(1, 10).bufferUntil(i -> i % 2 == 0).subscribe(System.out::println);\n        Flux.range(1, 10).bufferWhile(i -> i % 2 == 0).subscribe(System.out::println);\n\n        Flux.just(\"a\", \"b\", \"c\", \"d\")\n                .zipWith(Flux.just(\"e\", \"f\", \"g\", \"h\", \"i\"))\n                .subscribe(System.out::println);\n\n        Flux.just(\"a\", \"b\", \"c\", \"d\")\n                .zipWith(Flux.just(\"e\", \"f\", \"g\", \"h\", \"i\"), (s1, s2) -> String.format(\"%s-%s\", s1, s2))\n                .subscribe(System.out::println);\n\n\n        Flux.just(1, 2)\n                .concatWith(Mono.error(new IllegalStateException()))\n                .subscribe(System.out::println, System.err::println);\n\n        Flux.just(1, 2)\n                .concatWith(Mono.error(new IllegalStateException()))\n                .onErrorReturn(0)\n                .subscribe(System.out::println);\n\n\n        Flux.just(1, 2)\n                .concatWith(Mono.error(new IllegalArgumentException()))\n                .onErrorResume(e -> {\n                    if (e instanceof IllegalStateException) {\n                        return Mono.just(0);\n                    } else if (e instanceof IllegalArgumentException) {\n                        return Mono.just(-1);\n                    }\n                    return Mono.empty();\n                }).subscribe(System.out::println);\n\n        Thread.currentThread().join(20000);\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/MonoFluxTest.java",
    "content": "package com.example.webflux;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.reactivestreams.Subscriber;\nimport org.reactivestreams.Subscription;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\npublic class MonoFluxTest {\n\n    public static void main(String[] args) {\n        Subscriber<Integer> subscriber = new Subscriber<Integer>() {\n            private Subscription subscription;\n\n            @Override\n            public void onSubscribe(Subscription subscription) {\n                this.subscription = subscription;\n                this.subscription.request(1);\n            }\n\n            @Override\n            public void onNext(Integer item) {\n                System.out.println(\"接受到数据: \" + item);\n                try {\n                    TimeUnit.SECONDS.sleep(3);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                this.subscription.request(1);\n            }\n\n            @Override\n            public void onError(Throwable throwable) {\n                throwable.printStackTrace();\n                this.subscription.cancel();\n            }\n\n            @Override\n            public void onComplete() {\n                System.out.println(\"处理完了!\");\n            }\n\n        };\n\n        String[] strs = {\"1\", \"2\", \"3\"};\n        Flux.fromArray(strs).map(Integer::parseInt).subscribe(subscriber);\n        Mono.fromSupplier(() -> 1).map(s -> s + 1).subscribe(subscriber);\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/MonoTest.java",
    "content": "package com.example.webflux;\n\nimport reactor.core.publisher.Mono;\n\nimport java.util.Optional;\n\n/**\n * @author MrBird\n */\npublic class MonoTest {\n\n    public static void main(String[] args) {\n        Mono.just(\"are\").subscribe(System.out::println);\n        Mono.empty().subscribe(System.out::println);\n        Mono.fromSupplier(() -> \"you\").subscribe(System.out::println);\n        Mono.justOrEmpty(Optional.of(\"ok\")).subscribe(System.out::println);\n\n        Mono.create(sink -> sink.success(\"Hello\")).subscribe(System.out::println);\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/TestController.java",
    "content": "package com.example.webflux;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.IntStream;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class TestController {\n    // Mono 表示 0-1 个元素，Flux 0-N 个元素\n\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @GetMapping(\"sync\")\n    public String sync() {\n        logger.info(\"sync method start\");\n        String result = this.execute();\n        logger.info(\"sync method end\");\n        return result;\n    }\n\n    @GetMapping(\"async/mono\")\n    public Mono<String> asyncMono() {\n        logger.info(\"async method start\");\n        Mono<String> result = Mono.fromSupplier(this::execute);\n        logger.info(\"async method end\");\n        return result;\n    }\n\n    // SSE(Server Sent Event)\n    // https://developer.mozilla.org/zh-CN/docs/Server-sent_events/Using_server-sent_events\n    // http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html\n    @GetMapping(value = \"async/flux\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<String> asyncFlux() {\n        logger.info(\"async method start\");\n        Flux<String> result = Flux.fromStream(IntStream.range(1, 5).mapToObj(i -> {\n            try {\n                TimeUnit.SECONDS.sleep(1);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            return \"int value：\" + i;\n        }));\n        logger.info(\"async method end\");\n        return result;\n    }\n\n    private String execute() {\n        try {\n            TimeUnit.SECONDS.sleep(2);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        return \"hello\";\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/ViewController.java",
    "content": "package com.example.webflux;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n/**\n * @author MrBird\n */\n@Controller\npublic class ViewController {\n\n    @GetMapping(\"flux\")\n    public String flux() {\n        return \"flux\";\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/java/com/example/webflux/WebfluxApplication.java",
    "content": "package com.example.webflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class WebfluxApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(WebfluxApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/resources/application.yml",
    "content": "\n"
  },
  {
    "path": "57.Spring-Boot-WebFlux/webflux/src/main/resources/templates/flux.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>test sse</title>\n</head>\n<body>\n</body>\n<script>\n    var es = new EventSource(\"async/flux\");\n    es.onmessage = function (evt) {\n        console.log(evt.data);\n        if (evt.data === \"int value：4\") {\n            es.close();\n        }\n    };\n</script>\n</html>"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>webflux</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>webflux</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/java/com/example/webflux/WebfluxApplication.java",
    "content": "package com.example.webflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;\n\n@SpringBootApplication\n@EnableReactiveMongoRepositories\npublic class WebfluxApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(WebfluxApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/java/com/example/webflux/controller/UserController.java",
    "content": "package com.example.webflux.controller;\n\nimport com.example.webflux.domain.User;\nimport com.example.webflux.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"user\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    /**\n     * 以数组的形式一次性返回所有数据\n     */\n    @GetMapping\n    public Flux<User> getUsers() {\n        return userService.getUsers();\n    }\n\n    /**\n     * 以 Server sent events形式多次返回数据\n     */\n    @GetMapping(value = \"/stream\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<User> getUsersStream() {\n        return userService.getUsers();\n    }\n\n    @PostMapping\n    public Mono<User> createUser(User user) {\n        return userService.createUser(user);\n    }\n\n    /**\n     * 存在返回 200，不存在返回 404\n     */\n    @DeleteMapping(\"/{id}\")\n    public Mono<ResponseEntity<Void>> deleteUser(@PathVariable String id) {\n        return userService.deleteUser(id)\n                .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))\n                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));\n    }\n\n    /**\n     * 存在返回修改后的 User\n     * 不存在返回 404\n     */\n    @PutMapping(\"/{id}\")\n    public Mono<ResponseEntity<User>> updateUser(@PathVariable String id, User user) {\n        return userService.updateUser(id, user)\n                .map(u -> new ResponseEntity<>(u, HttpStatus.OK))\n                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));\n    }\n\n    /**\n     * 根据用户 id查找\n     * 存在返回，不存在返回 404\n     */\n    @GetMapping(\"/{id}\")\n    public Mono<ResponseEntity<User>> getUser(@PathVariable String id) {\n        return userService.getUser(id)\n                .map(user -> new ResponseEntity<>(user, HttpStatus.OK))\n                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));\n    }\n\n    /**\n     * 根据年龄段来查找\n     */\n    @GetMapping(\"/age/{from}/{to}\")\n    public Flux<User> getUserByAge(@PathVariable Integer from, @PathVariable Integer to) {\n        return userService.getUserByAge(from, to);\n    }\n\n    @GetMapping(value = \"/stream/age/{from}/{to}\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<User> getUserByAgeStream(@PathVariable Integer from, @PathVariable Integer to) {\n        return userService.getUserByAge(from, to);\n    }\n\n    /**\n     * 根据用户名查找\n     */\n    @GetMapping(\"/name/{name}\")\n    public Flux<User> getUserByName(@PathVariable String name) {\n        return userService.getUserByName(name);\n    }\n\n    @GetMapping(value = \"/stream/name/{name}\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<User> getUserByNameStream(@PathVariable String name) {\n        return userService.getUserByName(name);\n    }\n\n    /**\n     * 根据用户描述模糊查找\n     */\n    @GetMapping(\"/description/{description}\")\n    public Flux<User> getUserByDescription(@PathVariable String description) {\n        return userService.getUserByDescription(description);\n    }\n\n    @GetMapping(value = \"/stream/description/{description}\", produces = MediaType.TEXT_EVENT_STREAM_VALUE)\n    public Flux<User> getUserByDescriptionStream(@PathVariable String description) {\n        return userService.getUserByDescription(description);\n    }\n\n    /**\n     * 根据多个检索条件查询\n     */\n    @GetMapping(\"/condition\")\n    public Flux<User> getUserByCondition(int size, int page, User user) {\n        return userService.getUserByCondition(size, page, user);\n    }\n\n    @GetMapping(\"/condition/count\")\n    public Mono<Long> getUserByConditionCount(User user) {\n        return userService.getUserByConditionCount(user);\n    }\n}\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/java/com/example/webflux/dao/UserDao.java",
    "content": "package com.example.webflux.dao;\n\nimport com.example.webflux.domain.User;\nimport org.springframework.data.mongodb.repository.ReactiveMongoRepository;\nimport org.springframework.stereotype.Repository;\nimport reactor.core.publisher.Flux;\n\n/**\n * @author MrBird\n */\n@Repository\npublic interface UserDao extends ReactiveMongoRepository<User, String> {\n\n    /**\n     * 根据年龄段来查找\n     *\n     * @param from from\n     * @param to   to\n     * @return Flux<User>\n     */\n    Flux<User> findByAgeBetween(Integer from, Integer to);\n\n    /**\n     * 更具描述来模糊查询用户\n     *\n     * @param description 描述\n     * @return Flux<User>\n     */\n    Flux<User> findByDescriptionIsLike(String description);\n\n    /**\n     * 通过用户名查询\n     *\n     * @param name 用户名\n     * @return Flux<User>\n     */\n    Flux<User> findByNameEquals(String name);\n\n}\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/java/com/example/webflux/domain/User.java",
    "content": "package com.example.webflux.domain;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\n/**\n * @author MrBird\n */\n@Document(collection = \"user\")\npublic class User {\n\n    @Id\n    private String id;\n    private String name;\n    private Integer age;\n    private String description;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n}\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/java/com/example/webflux/service/UserService.java",
    "content": "package com.example.webflux.service;\n\nimport com.example.webflux.dao.UserDao;\nimport com.example.webflux.domain.User;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.data.mongodb.core.ReactiveMongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StringUtils;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * @author MrBird\n */\n@Service\npublic class UserService {\n\n    @Autowired\n    private UserDao userDao;\n    @Autowired\n    private ReactiveMongoTemplate template;\n\n    public Flux<User> getUsers() {\n        return userDao.findAll();\n    }\n\n    public Mono<User> getUser(String id) {\n        return this.userDao.findById(id);\n    }\n\n    /**\n     * 新增和修改都是 save方法，\n     * id 存在为修改，id 不存在为新增\n     */\n    public Mono<User> createUser(User user) {\n        return userDao.save(user);\n    }\n\n    public Mono<Void> deleteUser(String id) {\n        return this.userDao.findById(id)\n                .flatMap(user -> this.userDao.delete(user));\n    }\n\n    public Mono<User> updateUser(String id, User user) {\n        return this.userDao.findById(id)\n                .flatMap(u -> {\n                    u.setName(user.getName());\n                    u.setAge(user.getAge());\n                    u.setDescription(user.getDescription());\n                    return this.userDao.save(u);\n                });\n    }\n\n    public Flux<User> getUserByAge(Integer from, Integer to) {\n        return this.userDao.findByAgeBetween(from, to);\n    }\n\n    public Flux<User> getUserByName(String name) {\n        return this.userDao.findByNameEquals(name);\n    }\n\n    public Flux<User> getUserByDescription(String description) {\n        return this.userDao.findByDescriptionIsLike(description);\n    }\n\n    /**\n     * 分页查询，只返回分页后的数据，count值需要通过 getUserByConditionCount\n     * 方法获取\n     */\n    public Flux<User> getUserByCondition(int size, int page, User user) {\n        Query query = getQuery(user);\n        Sort sort = new Sort(Sort.Direction.DESC, \"age\");\n        Pageable pageable = PageRequest.of(page, size, sort);\n\n        return template.find(query.with(pageable), User.class);\n    }\n\n    /**\n     * 返回 count，配合 getUserByCondition使用\n     */\n    public Mono<Long> getUserByConditionCount(User user) {\n        Query query = getQuery(user);\n        return template.count(query, User.class);\n    }\n\n    private Query getQuery(User user) {\n        Query query = new Query();\n        Criteria criteria = new Criteria();\n\n        if (!StringUtils.isEmpty(user.getName())) {\n            criteria.and(\"name\").is(user.getName());\n        }\n        if (!StringUtils.isEmpty(user.getDescription())) {\n            criteria.and(\"description\").regex(user.getDescription());\n        }\n        query.addCriteria(criteria);\n        return query;\n    }\n}\n"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/src/main/resources/application.yml",
    "content": "spring:\n  data:\n    mongodb:\n      host: localhost\n      port: 27017\n      database: webflux"
  },
  {
    "path": "58.Spring-Boot-WebFlux-crud/webflux crud.postman_collection.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"8fa1e028-3088-4d19-bb08-4d41b3802487\",\n\t\t\"name\": \"webflux crud\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"createUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user?name=mike&description=python developer&age=21\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"name\",\n\t\t\t\t\t\t\t\"value\": \"mike\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"python developer\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"age\",\n\t\t\t\t\t\t\t\"value\": \"21\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUsers\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca56280f08f0b6048fd470b\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"updateUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"PUT\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca40ee8f08f0b68cc06c001?id=5ca56280f08f0b6048fd470b&description=Java gosu&age=19\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca40ee8f08f0b68cc06c001\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"id\",\n\t\t\t\t\t\t\t\"value\": \"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"Java gosu\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"age\",\n\t\t\t\t\t\t\t\"value\": \"19\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"deleteUser\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"DELETE\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/5ca56280f08f0b6048fd470b\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"5ca56280f08f0b6048fd470b\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUsersStream\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/stream\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"stream\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByAge\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/age/20/30\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"age\",\n\t\t\t\t\t\t\"20\",\n\t\t\t\t\t\t\"30\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByName\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/name/mrbird\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"mrbird\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByDescription\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/description/ui\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"description\",\n\t\t\t\t\t\t\"ui\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"getUserByCondition\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/user/condition?size=2&page=0\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"user\",\n\t\t\t\t\t\t\"condition\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"name\",\n\t\t\t\t\t\t\t\"value\": \"mrbird\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"description\",\n\t\t\t\t\t\t\t\"value\": \"r\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"size\",\n\t\t\t\t\t\t\t\"value\": \"2\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"page\",\n\t\t\t\t\t\t\t\"value\": \"0\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t]\n}"
  },
  {
    "path": "59.Spring-Security-SessionManager/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.session.MySessionExpiredStrategy;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport cc.mrbird.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n    @Autowired\n    private MySessionExpiredStrategy sessionExpiredStrategy;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n\n        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n            .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                    // http.httpBasic() // HTTP Basic\n                    .loginPage(\"/authentication/require\") // 登录跳转 URL\n                    .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                    .successHandler(authenticationSucessHandler) // 处理登录成功\n                    .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                    .authorizeRequests() // 授权配置\n                    .antMatchers(\"/authentication/require\",\n                            \"/login.html\", \"/code/image\",\"/code/sms\",\"/session/invalid\").permitAll() // 无需认证的请求路径\n                    .anyRequest()  // 所有请求\n                    .authenticated() // 都需要认证\n                .and()\n                    .sessionManagement() // 添加 Session管理器\n                    .invalidSessionUrl(\"/session/invalid\") // Session失效后跳转到这个链接\n                    .maximumSessions(1)\n                    .maxSessionsPreventsLogin(true)\n                    .expiredSessionStrategy(sessionExpiredStrategy)\n                .and()\n                .and()\n                    .csrf().disable()\n                .apply(smsAuthenticationConfig); // 将短信验证码认证配置加到 Spring Security 中\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/session/MySessionExpiredStrategy.java",
    "content": "package cc.mrbird.session;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.session.SessionInformationExpiredEvent;\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {\n\n    @Override\n    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {\n        HttpServletResponse response = event.getResponse();\n        response.setStatus(HttpStatus.UNAUTHORIZED.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(\"您的账号已经在别的地方登录，当前登录已失效。如果密码遭到泄露，请立即修改密码！\");\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\npublic class ImageCode implements Serializable {\n\n    private static final long serialVersionUID = -7831615057416168810L;\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    public ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport java.time.LocalDateTime;\n\npublic class SmsCode {\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public SmsCode(String code, int expireIn) {\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public SmsCode(String code, LocalDateTime expireTime) {\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.validate.code.ValidateCodeException;\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n\n        SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n}"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n\n    @GetMapping(\"/session/invalid\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String sessionInvalid(){\n        return \"session已失效，请重新认证\";\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport cc.mrbird.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    public final static String SESSION_KEY_SMS_CODE = \"SESSION_KEY_SMS_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        ImageCode codeInRedis = new ImageCode(null,imageCode.getCode(),imageCode.getExpireTime());\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, codeInRedis);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) {\n        SmsCode smsCode = createSMSCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_SMS_CODE + mobile, smsCode);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"您的登录验证码为：\" + smsCode.getCode() + \"，有效时间为60秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code, 60);\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n\nserver:\n  session:\n    timeout: 3600\n\nspring:\n  session:\n    store-type: redis"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "59.Spring-Security-SessionManager/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n\n<form class=\"login-page\" action=\"/login/mobile\" method=\"post\">\n    <div class=\"form\">\n        <h3>短信验证码登录</h3>\n        <input type=\"text\" placeholder=\"手机号\" name=\"mobile\" value=\"17777777777\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"smsCode\" placeholder=\"短信验证码\" style=\"width: 50%;\"/>\n            <a href=\"/code/sms?mobile=17777777777\">发送验证码</a>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "60.Spring-Security-Logout/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/handler/MyLogOutSuccessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyLogOutSuccessHandler implements LogoutSuccessHandler {\n\n    @Override\n    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {\n        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n        httpServletResponse.setContentType(\"application/json;charset=utf-8\");\n        httpServletResponse.getWriter().write(\"退出成功，请重新登录\");\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.handler.MyLogOutSuccessHandler;\nimport cc.mrbird.session.MySessionExpiredStrategy;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport cc.mrbird.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n    @Autowired\n    private MySessionExpiredStrategy sessionExpiredStrategy;\n\n    @Autowired\n    private MyLogOutSuccessHandler logOutSuccessHandler;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n\n        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n            .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                    // http.httpBasic() // HTTP Basic\n                    .loginPage(\"/authentication/require\") // 登录跳转 URL\n                    .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                    .successHandler(authenticationSucessHandler) // 处理登录成功\n                    .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                    .authorizeRequests() // 授权配置\n                    .antMatchers(\"/authentication/require\",\n                            \"/login.html\", \"/code/image\",\"/code/sms\",\"/session/invalid\", \"/signout/success\").permitAll() // 无需认证的请求路径\n                    .anyRequest()  // 所有请求\n                    .authenticated() // 都需要认证\n                .and()\n                    .sessionManagement() // 添加 Session管理器\n                    .invalidSessionUrl(\"/session/invalid\") // Session失效后跳转到这个链接\n                    .maximumSessions(1)\n                    .maxSessionsPreventsLogin(true)\n                    .expiredSessionStrategy(sessionExpiredStrategy)\n                .and()\n                .and()\n                    .logout()\n                    .logoutUrl(\"/signout\")\n                    // .logoutSuccessUrl(\"/signout/success\")\n                    .logoutSuccessHandler(logOutSuccessHandler)\n                    .deleteCookies(\"JSESSIONID\")\n                .and()\n                    .csrf().disable()\n                .apply(smsAuthenticationConfig); // 将短信验证码认证配置加到 Spring Security 中\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/session/MySessionExpiredStrategy.java",
    "content": "package cc.mrbird.session;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.session.SessionInformationExpiredEvent;\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {\n\n    @Override\n    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {\n        HttpServletResponse response = event.getResponse();\n        response.setStatus(HttpStatus.UNAUTHORIZED.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(\"您的账号已经在别的地方登录，当前登录已失效。如果密码遭到泄露，请立即修改密码！\");\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\npublic class ImageCode implements Serializable {\n\n    private static final long serialVersionUID = -7831615057416168810L;\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    public ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport java.time.LocalDateTime;\n\npublic class SmsCode {\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public SmsCode(String code, int expireIn) {\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public SmsCode(String code, LocalDateTime expireTime) {\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.validate.code.ValidateCodeException;\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n\n        SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n}"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n\n    @GetMapping(\"/session/invalid\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String sessionInvalid() {\n        return \"session已失效，请重新认证\";\n    }\n\n    @GetMapping(\"/signout/success\")\n    public String signout() {\n        return \"退出成功，请重新登录\";\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport cc.mrbird.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    public final static String SESSION_KEY_SMS_CODE = \"SESSION_KEY_SMS_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        ImageCode codeInRedis = new ImageCode(null,imageCode.getCode(),imageCode.getExpireTime());\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, codeInRedis);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) {\n        SmsCode smsCode = createSMSCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_SMS_CODE + mobile, smsCode);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"您的登录验证码为：\" + smsCode.getCode() + \"，有效时间为60秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code, 60);\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n\nserver:\n  session:\n    timeout: 3600\n\nspring:\n  session:\n    store-type: redis"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "60.Spring-Security-Logout/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n\n<form class=\"login-page\" action=\"/login/mobile\" method=\"post\">\n    <div class=\"form\">\n        <h3>短信验证码登录</h3>\n        <input type=\"text\" placeholder=\"手机号\" name=\"mobile\" value=\"17777777777\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"smsCode\" placeholder=\"短信验证码\" style=\"width: 50%;\"/>\n            <a href=\"/code/sms?mobile=17777777777\">发送验证码</a>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "61.Spring-security-Permission/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>Security</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>jar</packaging>\n\n    <name>Security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.14.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.social</groupId>\n            <artifactId>spring-social-config</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n\n</project>\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/SecurityApplication.java",
    "content": "package cc.mrbird;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/domain/MyUser.java",
    "content": "package cc.mrbird.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/handler/MyAuthenticationAccessDeniedHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.access.AccessDeniedHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationAccessDeniedHandler implements AccessDeniedHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(\"很抱歉，您没有该访问权限\");\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    // private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n    //\n    // @Autowired\n    // private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,\n                                        Authentication authentication) throws IOException {\n        // response.setContentType(\"application/json;charset=utf-8\");\n        // response.getWriter().write(mapper.writeValueAsString(authentication));\n        // SavedRequest savedRequest = requestCache.getRequest(request, response);\n        // System.out.println(savedRequest.getRedirectUrl());\n        // redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());\n        redirectStrategy.sendRedirect(request, response, \"/index\");\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/handler/MyLogOutSuccessHandler.java",
    "content": "package cc.mrbird.handler;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyLogOutSuccessHandler implements LogoutSuccessHandler {\n\n    @Override\n    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {\n        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());\n        httpServletResponse.setContentType(\"application/json;charset=utf-8\");\n        httpServletResponse.getWriter().write(\"退出成功，请重新登录\");\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/security/browser/BrowserSecurityConfig.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.handler.MyAuthenticationAccessDeniedHandler;\nimport cc.mrbird.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.handler.MyLogOutSuccessHandler;\nimport cc.mrbird.session.MySessionExpiredStrategy;\nimport cc.mrbird.validate.code.ValidateCodeFilter;\nimport cc.mrbird.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n@Configuration\n@EnableGlobalMethodSecurity(prePostEnabled = true)\npublic class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n    @Autowired\n    private MyAuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;\n\n    @Autowired\n    private ValidateCodeFilter validateCodeFilter;\n\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n    @Autowired\n    private MySessionExpiredStrategy sessionExpiredStrategy;\n\n    @Autowired\n    private MyLogOutSuccessHandler logOutSuccessHandler;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.exceptionHandling()\n                .accessDeniedHandler(authenticationAccessDeniedHandler)\n            .and()\n                .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码校验过滤器\n            .addFilterBefore(smsCodeFilter,UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                    // http.httpBasic() // HTTP Basic\n                    .loginPage(\"/authentication/require\") // 登录跳转 URL\n                    .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                    .successHandler(authenticationSucessHandler) // 处理登录成功\n                    .failureHandler(authenticationFailureHandler) // 处理登录失败\n                .and()\n                    .authorizeRequests() // 授权配置\n                    .antMatchers(\"/authentication/require\",\n                            \"/login.html\", \"/code/image\",\"/code/sms\",\"/session/invalid\", \"/signout/success\").permitAll() // 无需认证的请求路径\n                    .anyRequest()  // 所有请求\n                    .authenticated() // 都需要认证\n                .and()\n                    .sessionManagement() // 添加 Session管理器\n                    .invalidSessionUrl(\"/session/invalid\") // Session失效后跳转到这个链接\n                    .maximumSessions(1)\n                    .maxSessionsPreventsLogin(true)\n                    .expiredSessionStrategy(sessionExpiredStrategy)\n                .and()\n                .and()\n                    .logout()\n                    .logoutUrl(\"/signout\")\n                    // .logoutSuccessUrl(\"/signout/success\")\n                    .logoutSuccessHandler(logOutSuccessHandler)\n                    .deleteCookies(\"JSESSIONID\")\n                .and()\n                    .csrf().disable()\n                .apply(smsAuthenticationConfig); // 将短信验证码认证配置加到 Spring Security 中\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/security/browser/UserDetailService.java",
    "content": "package cc.mrbird.security.browser;\n\nimport cc.mrbird.domain.MyUser;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        List<GrantedAuthority> authorities = new ArrayList<>();\n        if (StringUtils.equalsIgnoreCase(\"mrbird\", username)) {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\");\n        } else {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"test\");\n        }\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), authorities);\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/session/MySessionExpiredStrategy.java",
    "content": "package cc.mrbird.session;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.web.session.SessionInformationExpiredEvent;\nimport org.springframework.security.web.session.SessionInformationExpiredStrategy;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MySessionExpiredStrategy implements SessionInformationExpiredStrategy {\n\n    @Override\n    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {\n        HttpServletResponse response = event.getResponse();\n        response.setStatus(HttpStatus.UNAUTHORIZED.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(\"您的账号已经在别的地方登录，当前登录已失效。如果密码遭到泄露，请立即修改密码！\");\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/code/ImageCode.java",
    "content": "package cc.mrbird.validate.code;\n\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\nimport java.time.LocalDateTime;\n\npublic class ImageCode implements Serializable {\n\n    private static final long serialVersionUID = -7831615057416168810L;\n    private BufferedImage image;\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public ImageCode(BufferedImage image, String code, int expireIn) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {\n        this.image = image;\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public BufferedImage getImage() {\n        return image;\n    }\n\n    public void setImage(BufferedImage image) {\n        this.image = image;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/code/ValidateCodeException.java",
    "content": "package cc.mrbird.validate.code;\n\nimport org.springframework.security.core.AuthenticationException;\n\npublic class ValidateCodeException extends AuthenticationException {\n    private static final long serialVersionUID = 5022575393500654458L;\n\n    public ValidateCodeException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/code/ValidateCodeFilter.java",
    "content": "package cc.mrbird.validate.code;\n\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class ValidateCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"imageCode\");\n\n        if (StringUtils.isBlank(codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.security.browser.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport java.time.LocalDateTime;\n\npublic class SmsCode {\n\n    private String code;\n\n    private LocalDateTime expireTime;\n\n    public SmsCode(String code, int expireIn) {\n        this.code = code;\n        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);\n    }\n\n    public SmsCode(String code, LocalDateTime expireTime) {\n        this.code = code;\n        this.expireTime = expireTime;\n    }\n\n    boolean isExpire() {\n        return LocalDateTime.now().isAfter(expireTime);\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public LocalDateTime getExpireTime() {\n        return expireTime;\n    }\n\n    public void setExpireTime(LocalDateTime expireTime) {\n        this.expireTime = expireTime;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.validate.smscode;\n\nimport cc.mrbird.validate.code.ValidateCodeException;\nimport cc.mrbird.web.controller.ValidateController;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestBindingException;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (ValidateCodeException e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n\n        SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(servletWebRequest, ValidateController.SESSION_KEY_SMS_CODE + mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不能为空！\");\n        }\n        if (codeInSession == null) {\n            throw new ValidateCodeException(\"验证码不存在！\");\n        }\n        if (codeInSession.isExpire()) {\n            sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n            throw new ValidateCodeException(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInSession.getCode(), smsCodeInRequest)) {\n            throw new ValidateCodeException(\"验证码不正确！\");\n        }\n        sessionStrategy.removeAttribute(servletWebRequest, ValidateController.SESSION_KEY_IMAGE_CODE);\n\n    }\n}"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/web/controller/BrowserSecurityController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.security.web.DefaultRedirectStrategy;\nimport org.springframework.security.web.RedirectStrategy;\nimport org.springframework.security.web.savedrequest.HttpSessionRequestCache;\nimport org.springframework.security.web.savedrequest.RequestCache;\nimport org.springframework.security.web.savedrequest.SavedRequest;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class BrowserSecurityController {\n\n    private RequestCache requestCache = new HttpSessionRequestCache();\n\n    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();\n\n    @GetMapping(\"/authentication/require\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        SavedRequest savedRequest = requestCache.getRequest(request, response);\n        if (savedRequest != null) {\n            String targetUrl = savedRequest.getRedirectUrl();\n            if (StringUtils.endsWithIgnoreCase(targetUrl, \".html\"))\n                redirectStrategy.sendRedirect(request, response, \"/login.html\");\n        }\n        return \"访问的资源需要身份认证！\";\n    }\n\n    @GetMapping(\"/session/invalid\")\n    @ResponseStatus(HttpStatus.UNAUTHORIZED)\n    public String sessionInvalid() {\n        return \"session已失效，请重新认证\";\n    }\n\n    @GetMapping(\"/signout/success\")\n    public String signout() {\n        return \"退出成功，请重新登录\";\n    }\n\n    @GetMapping(\"/auth/admin\")\n    @PreAuthorize(\"hasAuthority('admin')\")\n    public String authenticationTest() {\n        return \"您拥有admin权限，可以查看\";\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/web/controller/TestController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n    @GetMapping(\"hello\")\n    public String hello() {\n        return \"hello spring security\";\n    }\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication) {\n        // return SecurityContextHolder.getContext().getAuthentication();\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/java/cc/mrbird/web/controller/ValidateController.java",
    "content": "package cc.mrbird.web.controller;\n\nimport cc.mrbird.validate.code.ImageCode;\nimport cc.mrbird.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.social.connect.web.HttpSessionSessionStrategy;\nimport org.springframework.social.connect.web.SessionStrategy;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.imageio.ImageIO;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.Random;\n\n@RestController\npublic class ValidateController {\n\n    public final static String SESSION_KEY_IMAGE_CODE = \"SESSION_KEY_IMAGE_CODE\";\n\n    public final static String SESSION_KEY_SMS_CODE = \"SESSION_KEY_SMS_CODE\";\n\n    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();\n\n    @GetMapping(\"/code/image\")\n    public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {\n        ImageCode imageCode = createImageCode();\n        ImageCode codeInRedis = new ImageCode(null,imageCode.getCode(),imageCode.getExpireTime());\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_IMAGE_CODE, codeInRedis);\n        ImageIO.write(imageCode.getImage(), \"jpeg\", response.getOutputStream());\n    }\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) {\n        SmsCode smsCode = createSMSCode();\n        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY_SMS_CODE + mobile, smsCode);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"您的登录验证码为：\" + smsCode.getCode() + \"，有效时间为60秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code, 60);\n    }\n\n    private ImageCode createImageCode() {\n        int width = 100; // 验证码图片宽度\n        int height = 36; // 验证码图片长度\n        int length = 4; // 验证码位数\n        int expireIn = 60; // 验证码有效时间 60s\n\n        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\n\n        Graphics g = image.getGraphics();\n\n        Random random = new Random();\n\n        g.setColor(getRandColor(200, 250));\n        g.fillRect(0, 0, width, height);\n        g.setFont(new Font(\"Times New Roman\", Font.ITALIC, 20));\n        g.setColor(getRandColor(160, 200));\n        for (int i = 0; i < 155; i++) {\n            int x = random.nextInt(width);\n            int y = random.nextInt(height);\n            int xl = random.nextInt(12);\n            int yl = random.nextInt(12);\n            g.drawLine(x, y, x + xl, y + yl);\n        }\n\n        StringBuilder sRand = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            String rand = String.valueOf(random.nextInt(10));\n            sRand.append(rand);\n            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));\n            g.drawString(rand, 13 * i + 6, 16);\n        }\n\n        g.dispose();\n\n        return new ImageCode(image, sRand.toString(), expireIn);\n    }\n\n    private Color getRandColor(int fc, int bc) {\n        Random random = new Random();\n        if (fc > 255)\n            fc = 255;\n\n        if (bc > 255)\n            bc = 255;\n        int r = fc + random.nextInt(bc - fc);\n        int g = fc + random.nextInt(bc - fc);\n        int b = fc + random.nextInt(bc - fc);\n        return new Color(r, g, b);\n    }\n\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/resources/application.yml",
    "content": "security:\n  basic:\n    enabled: true\n\nserver:\n  session:\n    timeout: 3600\n\nspring:\n  session:\n    store-type: redis"
  },
  {
    "path": "61.Spring-security-Permission/src/main/resources/resources/css/login.css",
    "content": ".login-page {\n    width: 360px;\n    padding: 8% 0 0;\n    margin: auto;\n}\n.form {\n    position: relative;\n    z-index: 1;\n    background: #ffffff;\n    max-width: 360px;\n    margin: 0 auto 100px;\n    padding: 45px;\n    text-align: center;\n    box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);\n}\n.form input {\n    outline: 0;\n    background: #f2f2f2;\n    width: 100%;\n    border: 0;\n    margin: 0 0 15px;\n    padding: 15px;\n    box-sizing: border-box;\n    font-size: 14px;\n}\n.form button {\n    text-transform: uppercase;\n    outline: 0;\n    background: #4caf50;\n    width: 100%;\n    border: 0;\n    padding: 15px;\n    color: #ffffff;\n    font-size: 14px;\n    -webkit-transition: all 0.3 ease;\n    transition: all 0.3 ease;\n    cursor: pointer;\n}\n.form button:hover,\n.form button:active,\n.form button:focus {\n    background: #43a047;\n}\n.form .message {\n    margin: 15px 0 0;\n    color: #b3b3b3;\n    font-size: 12px;\n}\n.form .message a {\n    color: #4caf50;\n    text-decoration: none;\n}\n.form .register-form {\n    display: none;\n}\n.container {\n    position: relative;\n    z-index: 1;\n    max-width: 300px;\n    margin: 0 auto;\n}\n.container:before,\n.container:after {\n    content: \"\";\n    display: block;\n    clear: both;\n}\n.container .info {\n    margin: 50px auto;\n    text-align: center;\n}\n.container .info h1 {\n    margin: 0 0 15px;\n    padding: 0;\n    font-size: 36px;\n    font-weight: 300;\n    color: #1a1a1a;\n}\n.container .info span {\n    color: #4d4d4d;\n    font-size: 12px;\n}\n.container .info span a {\n    color: #000000;\n    text-decoration: none;\n}\n.container .info span .fa {\n    color: #ef3b3a;\n}\nbody {\n    background: #76b852; /* fallback for old browsers */\n    background: -webkit-linear-gradient(right, #76b852, #8dc26f);\n    background: -moz-linear-gradient(right, #76b852, #8dc26f);\n    background: -o-linear-gradient(right, #76b852, #8dc26f);\n    background: linear-gradient(to left, #76b852, #8dc26f);\n    font-family: Lato,\"PingFang SC\",\"Microsoft YaHei\",sans-serif;\n}\n"
  },
  {
    "path": "61.Spring-security-Permission/src/main/resources/resources/login.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登录</title>\n    <link rel=\"stylesheet\" href=\"css/login.css\" type=\"text/css\">\n</head>\n<body>\n<form class=\"login-page\" action=\"/login\" method=\"post\">\n    <div class=\"form\">\n        <h3>账户登录</h3>\n        <input type=\"text\" placeholder=\"用户名\" name=\"username\" required=\"required\"/>\n        <input type=\"password\" placeholder=\"密码\" name=\"password\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"imageCode\" placeholder=\"验证码\" style=\"width: 50%;\"/>\n            <img src=\"/code/image\"/>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n\n<form class=\"login-page\" action=\"/login/mobile\" method=\"post\">\n    <div class=\"form\">\n        <h3>短信验证码登录</h3>\n        <input type=\"text\" placeholder=\"手机号\" name=\"mobile\" value=\"17777777777\" required=\"required\"/>\n        <span style=\"display: inline\">\n            <input type=\"text\" name=\"smsCode\" placeholder=\"短信验证码\" style=\"width: 50%;\"/>\n            <a href=\"/code/sms?mobile=17777777777\">发送验证码</a>\n        </span>\n        <button type=\"submit\">登录</button>\n    </div>\n</form>\n</body>\n</html>"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.0.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>com.example</groupId>\n    <artifactId>demo</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>springboot-shiro-jwt-demo</name>\n    <description>springboot整合shiro，jwt简单样例</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <!-- shiro-spring -->\n        <dependency>\n            <groupId>org.apache.shiro</groupId>\n            <artifactId>shiro-spring</artifactId>\n            <version>1.4.0</version>\n        </dependency>\n        <!-- jwt -->\n        <dependency>\n            <groupId>com.auth0</groupId>\n            <artifactId>java-jwt</artifactId>\n            <version>3.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/DemoApplication.java",
    "content": "package com.example.demo;\n\nimport com.example.demo.properties.SystemProperties;\nimport org.springframework.boot.Banner;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\n\n/**\n *\n * @author MrBird\n */\n@SpringBootApplication\n@EnableConfigurationProperties(SystemProperties.class)\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(DemoApplication.class)\n                .bannerMode(Banner.Mode.OFF)\n                .run(args);\n    }\n\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/authentication/JWTFilter.java",
    "content": "package com.example.demo.authentication;\n\nimport com.example.demo.properties.SystemProperties;\nimport com.example.demo.utils.SpringContextUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authz.UnauthorizedException;\nimport org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.web.bind.annotation.RequestMethod;\n\nimport javax.servlet.ServletRequest;\nimport javax.servlet.ServletResponse;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n/**\n *\n * @author MrBird\n */\npublic class JWTFilter extends BasicHttpAuthenticationFilter {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    private static final String TOKEN = \"Token\";\n\n    private AntPathMatcher pathMatcher = new AntPathMatcher();\n\n    @Override\n    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {\n        HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n        SystemProperties properties = SpringContextUtil.getBean(SystemProperties.class);\n        String[] anonUrl = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getAnonUrl(), \",\");\n\n        boolean match = false;\n        for (String u : anonUrl) {\n            if (pathMatcher.match(u, httpServletRequest.getRequestURI()))\n                match = true;\n        }\n        if (match) return true;\n        if (isLoginAttempt(request, response)) {\n            return executeLogin(request, response);\n        }\n        return false;\n    }\n\n    @Override\n    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {\n        HttpServletRequest req = (HttpServletRequest) request;\n        String token = req.getHeader(TOKEN);\n        return token != null;\n    }\n\n    @Override\n    protected boolean executeLogin(ServletRequest request, ServletResponse response) {\n        HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n        String token = httpServletRequest.getHeader(TOKEN);\n        JWTToken jwtToken = new JWTToken(token);\n        try {\n            getSubject(request, response).login(jwtToken);\n            return true;\n        } catch (Exception e) {\n            log.error(e.getMessage());\n            return false;\n        }\n    }\n\n    /**\n     * 对跨域提供支持\n     */\n    @Override\n    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {\n        HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n        HttpServletResponse httpServletResponse = (HttpServletResponse) response;\n        httpServletResponse.setHeader(\"Access-control-Allow-Origin\", httpServletRequest.getHeader(\"Origin\"));\n        httpServletResponse.setHeader(\"Access-Control-Allow-Methods\", \"GET,POST,OPTIONS,PUT,DELETE\");\n        httpServletResponse.setHeader(\"Access-Control-Allow-Headers\", httpServletRequest.getHeader(\"Access-Control-Request-Headers\"));\n        // 跨域时会首先发送一个 option请求，这里我们给 option请求直接返回正常状态\n        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {\n            httpServletResponse.setStatus(HttpStatus.OK.value());\n            return false;\n        }\n        return super.preHandle(request, response);\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/authentication/JWTToken.java",
    "content": "package com.example.demo.authentication;\n\nimport org.apache.shiro.authc.AuthenticationToken;\n\n/**\n * JSON Web Token\n *\n * @author MrBird\n */\npublic class JWTToken implements AuthenticationToken {\n\n    private static final long serialVersionUID = 1282057025599826155L;\n\n    private String token;\n\n    private String exipreAt;\n\n    public JWTToken(String token) {\n        this.token = token;\n    }\n\n    public JWTToken(String token, String exipreAt) {\n        this.token = token;\n        this.exipreAt = exipreAt;\n    }\n\n    @Override\n    public Object getPrincipal() {\n        return token;\n    }\n\n    @Override\n    public Object getCredentials() {\n        return token;\n    }\n\n    public String getToken() {\n        return token;\n    }\n\n    public void setToken(String token) {\n        this.token = token;\n    }\n\n    public String getExipreAt() {\n        return exipreAt;\n    }\n\n    public void setExipreAt(String exipreAt) {\n        this.exipreAt = exipreAt;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/authentication/JWTUtil.java",
    "content": "package com.example.demo.authentication;\n\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.JWTVerifier;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport com.auth0.jwt.exceptions.JWTDecodeException;\nimport com.auth0.jwt.interfaces.DecodedJWT;\nimport com.example.demo.properties.SystemProperties;\nimport com.example.demo.utils.SpringContextUtil;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Date;\n\n/**\n *\n * @author MrBird\n */\npublic class JWTUtil {\n\n    private static Logger log = LoggerFactory.getLogger(JWTUtil.class);\n\n    private static final long EXPIRE_TIME = SpringContextUtil.getBean(SystemProperties.class).getJwtTimeOut() * 1000;\n\n    /**\n     * 校验 token是否正确\n     *\n     * @param token  密钥\n     * @param secret 用户的密码\n     * @return 是否正确\n     */\n    public static boolean verify(String token, String username, String secret) {\n        try {\n            Algorithm algorithm = Algorithm.HMAC256(secret);\n            JWTVerifier verifier = JWT.require(algorithm)\n                    .withClaim(\"username\", username)\n                    .build();\n            verifier.verify(token);\n            log.info(\"token is valid\");\n            return true;\n        } catch (Exception e) {\n            log.info(\"token is invalid{}\", e.getMessage());\n            return false;\n        }\n    }\n\n    /**\n     * 从 token中获取用户名\n     *\n     * @return token中包含的用户名\n     */\n    public static String getUsername(String token) {\n        try {\n            DecodedJWT jwt = JWT.decode(token);\n            return jwt.getClaim(\"username\").asString();\n        } catch (JWTDecodeException e) {\n            log.error(\"error：{}\", e.getMessage());\n            return null;\n        }\n    }\n\n    /**\n     * 生成 token\n     *\n     * @param username 用户名\n     * @param secret   用户的密码\n     * @return token\n     */\n    public static String sign(String username, String secret) {\n        try {\n            username = StringUtils.lowerCase(username);\n            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);\n            Algorithm algorithm = Algorithm.HMAC256(secret);\n            return JWT.create()\n                    .withClaim(\"username\", username)\n                    .withExpiresAt(date)\n                    .sign(algorithm);\n        } catch (Exception e) {\n            log.error(\"error：{}\", e);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/authentication/ShiroConfig.java",
    "content": "package com.example.demo.authentication;\n\nimport org.apache.shiro.mgt.SecurityManager;\nimport org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.servlet.Filter;\nimport java.util.LinkedHashMap;\n\n/**\n * Shiro 配置类\n *\n * @author MrBird\n */\n@Configuration\npublic class ShiroConfig {\n\n    @Bean\n    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {\n        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();\n        // 设置 securityManager\n        shiroFilterFactoryBean.setSecurityManager(securityManager);\n\n        // 在 Shiro过滤器链上加入 JWTFilter\n        LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();\n        filters.put(\"jwt\", new JWTFilter());\n        shiroFilterFactoryBean.setFilters(filters);\n\n        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();\n        // 所有请求都要经过 jwt过滤器\n        filterChainDefinitionMap.put(\"/**\", \"jwt\");\n\n        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);\n        return shiroFilterFactoryBean;\n    }\n\n    @Bean\n    public SecurityManager securityManager() {\n        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();\n        // 配置 SecurityManager，并注入 shiroRealm\n        securityManager.setRealm(shiroRealm());\n        return securityManager;\n    }\n\n    @Bean\n    public ShiroRealm shiroRealm() {\n        // 配置 Realm\n        return new ShiroRealm();\n    }\n\n    @Bean\n    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {\n        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();\n        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);\n        return authorizationAttributeSourceAdvisor;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/authentication/ShiroRealm.java",
    "content": "package com.example.demo.authentication;\n\nimport com.example.demo.domain.User;\nimport com.example.demo.utils.SystemUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authc.AuthenticationException;\nimport org.apache.shiro.authc.AuthenticationInfo;\nimport org.apache.shiro.authc.AuthenticationToken;\nimport org.apache.shiro.authc.SimpleAuthenticationInfo;\nimport org.apache.shiro.authz.AuthorizationInfo;\nimport org.apache.shiro.authz.SimpleAuthorizationInfo;\nimport org.apache.shiro.realm.AuthorizingRealm;\nimport org.apache.shiro.subject.PrincipalCollection;\n\n/**\n * 自定义实现 ShiroRealm，包含认证和授权两大模块\n *\n * @author MrBird\n */\npublic class ShiroRealm extends AuthorizingRealm {\n\n    @Override\n    public boolean supports(AuthenticationToken token) {\n        return token instanceof JWTToken;\n    }\n\n    /**\n     * `\n     * 授权模块，获取用户角色和权限\n     *\n     * @param token token\n     * @return AuthorizationInfo 权限信息\n     */\n    @Override\n    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection token) {\n        String username = JWTUtil.getUsername(token.toString());\n        User user = SystemUtils.getUser(username);\n\n        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();\n\n        // 获取用户角色集（模拟值，实际从数据库获取）\n        simpleAuthorizationInfo.setRoles(user.getRole());\n\n        // 获取用户权限集（模拟值，实际从数据库获取）\n        simpleAuthorizationInfo.setStringPermissions(user.getPermission());\n        return simpleAuthorizationInfo;\n    }\n\n    /**\n     * 用户认证\n     *\n     * @param authenticationToken 身份认证 token\n     * @return AuthenticationInfo 身份认证信息\n     * @throws AuthenticationException 认证相关异常\n     */\n    @Override\n    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {\n        // 这里的 token是从 JWTFilter 的 executeLogin 方法传递过来的，已经经过了解密\n        String token = (String) authenticationToken.getCredentials();\n\n        String username = JWTUtil.getUsername(token);\n\n        if (StringUtils.isBlank(username))\n            throw new AuthenticationException(\"token校验不通过\");\n\n        // 通过用户名查询用户信息\n        User user = SystemUtils.getUser(username);\n\n        if (user == null)\n            throw new AuthenticationException(\"用户名或密码错误\");\n        if (!JWTUtil.verify(token, username, user.getPassword()))\n            throw new AuthenticationException(\"token校验不通过\");\n        return new SimpleAuthenticationInfo(token, token, \"shiro_realm\");\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/controller/LoginController.java",
    "content": "package com.example.demo.controller;\n\nimport com.example.demo.authentication.JWTUtil;\nimport com.example.demo.domain.Response;\nimport com.example.demo.domain.User;\nimport com.example.demo.exception.SystemException;\nimport com.example.demo.properties.SystemProperties;\nimport com.example.demo.utils.MD5Util;\nimport com.example.demo.utils.SystemUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.constraints.NotBlank;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@RestController\n@Validated\npublic class LoginController {\n\n    @Autowired\n    private SystemProperties properties;\n\n    @PostMapping(\"/login\")\n    public Response login(\n            @NotBlank(message = \"{required}\") String username,\n            @NotBlank(message = \"{required}\") String password, HttpServletRequest request) throws Exception {\n        username = StringUtils.lowerCase(username);\n        password = MD5Util.encrypt(username, password);\n\n        final String errorMessage = \"用户名或密码错误\";\n        User user = SystemUtils.getUser(username);\n\n        if (user == null)\n            throw new SystemException(errorMessage);\n        if (!StringUtils.equals(user.getPassword(), password))\n            throw new SystemException(errorMessage);\n\n        // 生成 Token\n        String token = JWTUtil.sign(username, password);\n\n        Map<String, Object> userInfo = this.generateUserInfo(token, user);\n        return new Response().message(\"认证成功\").data(userInfo);\n    }\n\n    /**\n     * 生成前端需要的用户信息，包括：\n     * 1. token\n     * 2. user\n     *\n     * @param token token\n     * @param user  用户信息\n     * @return UserInfo\n     */\n    private Map<String, Object> generateUserInfo(String token, User user) {\n        String username = user.getUsername();\n        Map<String, Object> userInfo = new HashMap<>();\n        userInfo.put(\"token\", token);\n\n        user.setPassword(\"it's a secret\");\n        userInfo.put(\"user\", user);\n        return userInfo;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/controller/TestController.java",
    "content": "package com.example.demo.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresPermissions;\nimport org.apache.shiro.authz.annotation.RequiresRoles;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"test\")\npublic class TestController {\n\n    /**\n     * 需要登录才能访问\n     */\n    @GetMapping(\"/1\")\n    public String test1() {\n        return \"success\";\n    }\n\n    /**\n     * 需要 admin 角色才能访问\n     */\n    @GetMapping(\"/2\")\n    @RequiresRoles(\"admin\")\n    public String test2() {\n        return \"success\";\n    }\n\n    /**\n     * 需要 \"user:add\" 权限才能访问\n     */\n    @GetMapping(\"/3\")\n    @RequiresPermissions(\"user:add\")\n    public String test3() {\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/domain/Response.java",
    "content": "package com.example.demo.domain;\n\nimport java.util.HashMap;\n\n/**\n *\n * @author MrBird\n */\npublic class Response extends HashMap<String, Object> {\n\n    private static final long serialVersionUID = -8713837118340960775L;\n\n    public Response message(String message) {\n        this.put(\"message\", message);\n        return this;\n    }\n\n    public Response data(Object data) {\n        this.put(\"data\", data);\n        return this;\n    }\n\n    @Override\n    public Response put(String key, Object value) {\n        super.put(key, value);\n        return this;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/domain/User.java",
    "content": "package com.example.demo.domain;\n\nimport java.io.Serializable;\nimport java.util.Set;\n\n/**\n * @author MrBird\n */\npublic class User implements Serializable {\n    private static final long serialVersionUID = -2731598327208972274L;\n\n    private String username;\n\n    private String password;\n\n    private Set<String> role;\n\n    private Set<String> permission;\n\n    public User(String username, String password, Set<String> role, Set<String> permission) {\n        this.username = username;\n        this.password = password;\n        this.role = role;\n        this.permission = permission;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public Set<String> getRole() {\n        return role;\n    }\n\n    public void setRole(Set<String> role) {\n        this.role = role;\n    }\n\n    public Set<String> getPermission() {\n        return permission;\n    }\n\n    public void setPermission(Set<String> permission) {\n        this.permission = permission;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/exception/SystemException.java",
    "content": "package com.example.demo.exception;\n\n/**\n * 系统内部异常\n *\n * @author MrBird\n */\npublic class SystemException extends Exception {\n\n    private static final long serialVersionUID = -994962710559017255L;\n\n    public SystemException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/handler/GlobalExceptionHandler.java",
    "content": "package com.example.demo.handler;\n\nimport com.example.demo.domain.Response;\nimport com.example.demo.exception.SystemException;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.shiro.authz.UnauthorizedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.Ordered;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.validation.BindException;\nimport org.springframework.validation.FieldError;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseStatus;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.Path;\nimport java.util.List;\nimport java.util.Set;\n\n\n/**\n *\n * @author MrBird\n */\n@RestControllerAdvice\n@Order(value = Ordered.HIGHEST_PRECEDENCE)\npublic class GlobalExceptionHandler {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @ExceptionHandler(value = Exception.class)\n    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n    public Response handleException(Exception e) {\n        log.error(\"系统内部异常，异常信息：\", e);\n        return new Response().message(\"系统内部异常\");\n    }\n\n    @ExceptionHandler(value = SystemException.class)\n    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)\n    public Response handleParamsInvalidException(SystemException e) {\n        log.error(\"系统错误：{}\", e.getMessage());\n        return new Response().message(e.getMessage());\n    }\n\n    /**\n     * 统一处理请求参数校验(实体对象传参)\n     *\n     * @param e BindException\n     * @return FebsResponse\n     */\n    @ExceptionHandler(BindException.class)\n    @ResponseStatus(HttpStatus.BAD_REQUEST)\n    public Response validExceptionHandler(BindException e) {\n        StringBuilder message = new StringBuilder();\n        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();\n        for (FieldError error : fieldErrors) {\n            message.append(error.getField()).append(error.getDefaultMessage()).append(\",\");\n        }\n        message = new StringBuilder(message.substring(0, message.length() - 1));\n        return new Response().message(message.toString());\n\n    }\n\n    /**\n     * 统一处理请求参数校验(普通传参)\n     *\n     * @param e ConstraintViolationException\n     * @return FebsResponse\n     */\n    @ExceptionHandler(value = ConstraintViolationException.class)\n    @ResponseStatus(HttpStatus.BAD_REQUEST)\n    public Response handleConstraintViolationException(ConstraintViolationException e) {\n        StringBuilder message = new StringBuilder();\n        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();\n        for (ConstraintViolation<?> violation : violations) {\n            Path path = violation.getPropertyPath();\n            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), \".\");\n            message.append(pathArr[1]).append(violation.getMessage()).append(\",\");\n        }\n        message = new StringBuilder(message.substring(0, message.length() - 1));\n        return new Response().message(message.toString());\n    }\n\n    @ExceptionHandler(value = UnauthorizedException.class)\n    @ResponseStatus(HttpStatus.FORBIDDEN)\n    public void handleUnauthorizedException(Exception e) {\n        log.error(\"权限不足，{}\", e.getMessage());\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/properties/SystemProperties.java",
    "content": "package com.example.demo.properties;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n *\n * @author MrBird\n */\n@ConfigurationProperties(prefix = \"system\")\npublic class SystemProperties {\n\n    /**\n     * 免认证 URL\n     */\n    private String anonUrl;\n\n    /**\n     * token默认有效时间 1天\n     */\n    private Long jwtTimeOut = 86400L;\n\n    public String getAnonUrl() {\n        return anonUrl;\n    }\n\n    public void setAnonUrl(String anonUrl) {\n        this.anonUrl = anonUrl;\n    }\n\n    public Long getJwtTimeOut() {\n        return jwtTimeOut;\n    }\n\n    public void setJwtTimeOut(Long jwtTimeOut) {\n        this.jwtTimeOut = jwtTimeOut;\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/runner/PrintRunner.java",
    "content": "package com.example.demo.runner;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.ApplicationArguments;\nimport org.springframework.boot.ApplicationRunner;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class PrintRunner implements ApplicationRunner {\n\n    private Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public void run(ApplicationArguments args) {\n        logger.info(\"Provided by handsome 帅比裙主，详情见readme.md\");\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/utils/DateUtil.java",
    "content": "package com.example.demo.utils;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.Date;\nimport java.util.Locale;\n\n/**\n * 时间工具类\n *\n * @author MrBird\n */\npublic class DateUtil {\n\n    public static final String FULL_TIME_PATTERN = \"yyyyMMddHHmmss\";\n\n    public static final String FULL_TIME_SPLIT_PATTERN = \"yyyy-MM-dd HH:mm:ss\";\n\n    public static String formatFullTime(LocalDateTime localDateTime) {\n        return formatFullTime(localDateTime, FULL_TIME_PATTERN);\n    }\n\n    public static String formatFullTime(LocalDateTime localDateTime, String pattern) {\n        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);\n        return localDateTime.format(dateTimeFormatter);\n    }\n\n    private static String getDateFormat(Date date, String dateFormatType) {\n        SimpleDateFormat simformat = new SimpleDateFormat(dateFormatType);\n        return simformat.format(date);\n    }\n\n    public static String formatCSTTime(String date, String format) throws ParseException {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"EEE MMM dd HH:mm:ss zzz yyyy\", Locale.US);\n        Date d = sdf.parse(date);\n        return DateUtil.getDateFormat(d, format);\n    }\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/utils/MD5Util.java",
    "content": "package com.example.demo.utils;\n\nimport org.apache.shiro.crypto.hash.SimpleHash;\nimport org.apache.shiro.util.ByteSource;\n\n/**\n *\n * @author MrBird\n */\npublic class MD5Util {\n\n\tprotected MD5Util(){\n\n\t}\n\n\tprivate static final String ALGORITH_NAME = \"md5\";\n\n\tprivate static final int HASH_ITERATIONS = 2;\n\n\tpublic static String encrypt(String password) {\n\t\treturn new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(password), HASH_ITERATIONS).toHex();\n\t}\n\n\tpublic static String encrypt(String username, String password) {\n\t\treturn new SimpleHash(ALGORITH_NAME, password, ByteSource.Util.bytes(username.toLowerCase() + password),\n\t\t\t\tHASH_ITERATIONS).toHex();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println(encrypt(\"scott\",\"123456\"));\n\t}\n\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/utils/SpringContextUtil.java",
    "content": "package com.example.demo.utils;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n/**\n * Spring Context 工具类\n * \n * @author MrBird\n *\n */\n@Component\npublic class SpringContextUtil implements ApplicationContextAware {\n\tprivate static ApplicationContext applicationContext;\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n\t\tSpringContextUtil.applicationContext = applicationContext;\n\t}\n\n\tpublic static Object getBean(String name) {\n\t\treturn applicationContext.getBean(name);\n\t}\n\tpublic static <T> T getBean(Class<T> clazz){\n\t\treturn applicationContext.getBean(clazz);\n\t}\n\n\tpublic static <T> T getBean(String name, Class<T> requiredType) {\n\t\treturn applicationContext.getBean(name, requiredType);\n\t}\n\n\tpublic static boolean containsBean(String name) {\n\t\treturn applicationContext.containsBean(name);\n\t}\n\n\tpublic static boolean isSingleton(String name) {\n\t\treturn applicationContext.isSingleton(name);\n\t}\n\n\tpublic static Class<?> getType(String name) {\n\t\treturn applicationContext.getType(name);\n\t}\n\n}"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/java/com/example/demo/utils/SystemUtils.java",
    "content": "package com.example.demo.utils;\n\nimport com.example.demo.domain.User;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.*;\n\n/**\n * 系统工具类\n *\n * @author MrBird\n */\npublic class SystemUtils {\n\n    private static Logger log = LoggerFactory.getLogger(SystemUtils.class);\n\n    /**\n     * 模拟两个用户\n     *\n     * @return List<User>\n     */\n    private static List<User> users() {\n        List<User> users = new ArrayList<>();\n        // 模拟两个用户：\n        // 1. 用户名 admin，密码 123456，角色 admin（管理员），权限 \"user:add\"，\"user:view\"\n        // 1. 用户名 scott，密码 123456，角色 regist（注册用户），权限 \"user:view\"\n        users.add(new User(\n                \"admin\",\n                \"bfc62b3f67a4c3e57df84dad8cc48a3b\",\n                new HashSet<>(Collections.singletonList(\"admin\")),\n                new HashSet<>(Arrays.asList(\"user:add\", \"user:view\"))));\n        users.add(new User(\n                \"scott\",\n                \"11bd73355c7bbbac151e4e4f943e59be\",\n                new HashSet<>(Collections.singletonList(\"regist\")),\n                new HashSet<>(Collections.singletonList(\"user:view\"))));\n        return users;\n    }\n\n    /**\n     * 获取用户\n     *\n     * @param username 用户名\n     * @return 用户\n     */\n    public static User getUser(String username) {\n        List<User> users = SystemUtils.users();\n        return users.stream().filter(user -> StringUtils.equalsIgnoreCase(username, user.getUsername())).findFirst().orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/resources/ValidationMessages.properties",
    "content": "required=\\u4E0D\\u80FD\\u4E3A\\u7A7A"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/resources/application.yml",
    "content": "system:\n  # 后端免认证接口 url\n  anonUrl: /login\n  # token有效期，单位秒\n  jwtTimeOut: 3600\n\nspring:\n  aop:\n    proxy-target-class: true"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/resources/postman.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"37b4020a-9a03-4d5a-9504-9f6520a040f1\",\n\t\t\"name\": \"测试请求\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"登录测试\",\n\t\t\t\"event\": [\n\t\t\t\t{\n\t\t\t\t\t\"listen\": \"prerequest\",\n\t\t\t\t\t\"script\": {\n\t\t\t\t\t\t\"id\": \"465a0661-a8b5-4ddd-ad37-6a92f9a25348\",\n\t\t\t\t\t\t\"exec\": [\n\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"type\": \"text/javascript\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Content-Type\",\n\t\t\t\t\t\t\"name\": \"Content-Type\",\n\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\"value\": \"text/properties\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/login?username=admin&password=123456\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"login\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"username\",\n\t\t\t\t\t\t\t\"value\": \"admin\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"password\",\n\t\t\t\t\t\t\t\"value\": \"123456\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"admin请求测试\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Token\",\n\t\t\t\t\t\t\"value\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTE3MDIyNTgsInVzZXJuYW1lIjoiYWRtaW4ifQ.EyruxB9m6VJ2Rs_29iB1IblKD1H2fVTB2Nb0xQ4PYOc\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/test/2\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"test\",\n\t\t\t\t\t\t\"2\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"scott请求测试\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Token\",\n\t\t\t\t\t\t\"value\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTE3MDIxODMsInVzZXJuYW1lIjoic2NvdHQifQ._rR8kgvYPsnnZwMW6QdaSD8jw8clcWI0b0atd3oEdGY\",\n\t\t\t\t\t\t\"equals\": true\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/test/3?Token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTE3MDIxODMsInVzZXJuYW1lIjoic2NvdHQifQ._rR8kgvYPsnnZwMW6QdaSD8jw8clcWI0b0atd3oEdGY\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"test\",\n\t\t\t\t\t\t\"3\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"Token\",\n\t\t\t\t\t\t\t\"value\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTE3MDIxODMsInVzZXJuYW1lIjoic2NvdHQifQ._rR8kgvYPsnnZwMW6QdaSD8jw8clcWI0b0atd3oEdGY\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"测试/test/1\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Token\",\n\t\t\t\t\t\t\"value\": \"\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"body\": {\n\t\t\t\t\t\"mode\": \"raw\",\n\t\t\t\t\t\"raw\": \"\"\n\t\t\t\t},\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/test/1\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"test\",\n\t\t\t\t\t\t\"1\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t]\n}"
  },
  {
    "path": "62.Spring-Boot-Shiro-JWT/src/main/resources/readme.md",
    "content": "springboot & shiro & jwt 简单demo\n\n这是一个springboot+shiro+jwt的简单demo，无数据库无redis。\n系统内置模拟了两个用户：\n\n| 用户名        | 密码   |  角色  | 权限 |\n| :-----   | :-----  | :----  |:----\n| admin     | 123456 |   admin     | \"user:add\",\"user:view\"\n| scott        |   123456   |   regist   | \"user:view\"\n\n参加 com.example.demo.utils.SystemUtils#users\n\n测试样例使用postman导入resources/postman.json即可。"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.6.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>security</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-oauth2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/SecurityApplication.java",
    "content": "package cc.mrbird.security;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/config/AuthorizationServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableAuthorizationServer\npublic class AuthorizationServerConfig extends WebSecurityConfigurerAdapter {\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/config/ResourceServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableResourceServer\npublic class ResourceServerConfig  {\n\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/controller/UserController.java",
    "content": "package cc.mrbird.security.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class UserController {\n\n    @GetMapping(\"index\")\n    public Object index(Authentication authentication){\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/domain/MyUser.java",
    "content": "package cc.mrbird.security.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/java/cc/mrbird/security/service/UserDetailService.java",
    "content": "package cc.mrbird.security.service;\n\nimport cc.mrbird.security.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\"));\n    }\n}\n"
  },
  {
    "path": "63.Spring-Security-OAuth2-Guide/src/main/resources/application.yml",
    "content": "security:\n  oauth2:\n    client:\n      client-id: test\n      client-secret: test1234\n      registered-redirect-uri: http://mrbird.cc\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.6.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>security</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-oauth2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/SecurityApplication.java",
    "content": "package cc.mrbird.security;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/config/AuthorizationServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableAuthorizationServer\npublic class AuthorizationServerConfig {\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/config/ResourceServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport cc.mrbird.security.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.security.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.security.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.security.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableResourceServer\npublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                .successHandler(authenticationSucessHandler) // 处理登录成功\n                .failureHandler(authenticationFailureHandler) // 处理登录失败\n            .and()\n                .authorizeRequests() // 授权配置\n                .antMatchers(\"/code/sms\").permitAll()\n                .anyRequest()  // 所有请求\n                .authenticated() // 都需要认证\n            .and()\n                .csrf().disable()\n            .apply(smsAuthenticationConfig);\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/controller/UserController.java",
    "content": "package cc.mrbird.security.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class UserController {\n\n    @GetMapping(\"index\")\n    public Object index(@AuthenticationPrincipal Authentication authentication){\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/controller/ValidateController.java",
    "content": "package cc.mrbird.security.controller;\n\nimport cc.mrbird.security.service.RedisCodeService;\nimport cc.mrbird.security.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@RestController\npublic class ValidateController {\n\n    @Autowired\n    private RedisCodeService redisCodeService;\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) throws Exception {\n        SmsCode smsCode = createSMSCode();\n        redisCodeService.save(smsCode, new ServletWebRequest(request), mobile);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"手机号\" + mobile + \"的登录验证码为：\" + smsCode.getCode() + \"，有效时间为120秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code);\n    }\n\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/domain/MyUser.java",
    "content": "package cc.mrbird.security.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.security.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.security.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;\nimport org.springframework.security.oauth2.provider.*;\nimport org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.HashMap;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private ClientDetailsService clientDetailsService;\n    @Autowired\n    private AuthorizationServerTokenServices authorizationServerTokenServices;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {\n        // 1. 从请求头中获取 ClientId\n        String header = request.getHeader(\"Authorization\");\n        if (header == null || !header.startsWith(\"Basic \")) {\n            throw new UnapprovedClientAuthenticationException(\"请求头中无client信息\");\n        }\n\n        String[] tokens = this.extractAndDecodeHeader(header, request);\n        String clientId = tokens[0];\n        String clientSecret = tokens[1];\n\n        TokenRequest tokenRequest = null;\n\n        // 2. 通过 ClientDetailsService 获取 ClientDetails\n        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);\n\n        // 3. 校验 ClientId和 ClientSecret的正确性\n        if (clientDetails == null) {\n            throw new UnapprovedClientAuthenticationException(\"clientId:\" + clientId + \"对应的信息不存在\");\n        } else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {\n            throw new UnapprovedClientAuthenticationException(\"clientSecret不正确\");\n        } else {\n            // 4. 通过 TokenRequest构造器生成 TokenRequest\n            tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), \"custom\");\n        }\n\n        // 5. 通过 TokenRequest的 createOAuth2Request方法获取 OAuth2Request\n        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);\n        // 6. 通过 Authentication和 OAuth2Request构造出 OAuth2Authentication\n        OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);\n\n        // 7. 通过 AuthorizationServerTokenServices 生成 OAuth2AccessToken\n        OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(auth2Authentication);\n\n        // 8. 返回 Token\n        log.info(\"登录成功\");\n        response.setContentType(\"application/json;charset=UTF-8\");\n        response.getWriter().write(new ObjectMapper().writeValueAsString(token));\n    }\n\n    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) {\n        byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);\n\n        byte[] decoded;\n        try {\n            decoded = Base64.getDecoder().decode(base64Token);\n        } catch (IllegalArgumentException var7) {\n            throw new BadCredentialsException(\"Failed to decode basic authentication token\");\n        }\n\n        String token = new String(decoded, StandardCharsets.UTF_8);\n        int delim = token.indexOf(\":\");\n        if (delim == -1) {\n            throw new BadCredentialsException(\"Invalid basic authentication token\");\n        } else {\n            return new String[]{token.substring(0, delim), token.substring(delim + 1)};\n        }\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/service/RedisCodeService.java",
    "content": "package cc.mrbird.security.service;\n\nimport cc.mrbird.security.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Redis操作验证码服务\n */\n@Service\npublic class RedisCodeService {\n\n    private final static String SMS_CODE_PREFIX = \"SMS_CODE:\";\n    private final static Integer TIME_OUT = 300;\n\n    @Autowired\n    private StringRedisTemplate redisTemplate;\n\n    /**\n     * 保存验证码到 redis\n     *\n     * @param smsCode 短信验证码\n     * @param request ServletWebRequest\n     */\n    public void save(SmsCode smsCode, ServletWebRequest request, String mobile) throws Exception {\n        redisTemplate.opsForValue().set(key(request, mobile), smsCode.getCode(), TIME_OUT, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 获取验证码\n     *\n     * @param request ServletWebRequest\n     * @return 验证码\n     */\n    public String get(ServletWebRequest request, String mobile) throws Exception {\n        return redisTemplate.opsForValue().get(key(request, mobile));\n    }\n\n    /**\n     * 移除验证码\n     *\n     * @param request ServletWebRequest\n     */\n    public void remove(ServletWebRequest request, String mobile) throws Exception {\n        redisTemplate.delete(key(request, mobile));\n    }\n\n    private String key(ServletWebRequest request, String mobile) throws Exception {\n        String deviceId = request.getHeader(\"deviceId\");\n        if (StringUtils.isBlank(deviceId)) {\n            throw new Exception(\"请在请求头中设置deviceId\");\n        }\n        return SMS_CODE_PREFIX + deviceId + \":\" + mobile;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/service/UserDetailService.java",
    "content": "package cc.mrbird.security.service;\n\nimport cc.mrbird.security.domain.MyUser;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        List<GrantedAuthority> authorities = new ArrayList<>();\n        if (StringUtils.equalsIgnoreCase(\"mrbird\", username)) {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\");\n        } else {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"test\");\n        }\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), authorities);\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\npublic class SmsCode {\n\n    private String code;\n\n    public SmsCode(String code) {\n        this.code = code;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/java/cc/mrbird/security/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.RedisCodeService;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n    @Autowired\n    private RedisCodeService redisCodeService;\n\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (Exception e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new AuthenticationServiceException(e.getMessage()));\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws Exception {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"mobile\");\n\n        String codeInRedis = redisCodeService.get(servletWebRequest, mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new Exception(\"验证码不能为空！\");\n        }\n        if (codeInRedis == null) {\n            throw new Exception(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInRedis, smsCodeInRequest)) {\n            throw new Exception(\"验证码不正确！\");\n        }\n        redisCodeService.remove(servletWebRequest, mobileInRequest);\n\n    }\n}"
  },
  {
    "path": "64.Spring-Security-OAuth2-Customize/src/main/resources/application.yml",
    "content": "security:\n  oauth2:\n    client:\n      client-id: test\n      client-secret: test1234\n\n\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.6.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>security</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>security</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-oauth2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt</artifactId>\n            <version>0.9.1</version>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/security.postman_collection.json",
    "content": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"3e982fc6-c1cb-4459-bca3-5fa81b956a37\",\n\t\t\"name\": \"security\",\n\t\t\"schema\": \"https://schema.getpostman.com/json/collection/v2.1.0/collection.json\"\n\t},\n\t\"item\": [\n\t\t{\n\t\t\t\"name\": \"/oauth/token 密码模式\",\n\t\t\t\"request\": {\n\t\t\t\t\"auth\": {\n\t\t\t\t\t\"type\": \"noauth\"\n\t\t\t\t},\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"warning\": \"This is a duplicate header and will be overridden by the Authorization header generated by Postman.\",\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"value\": \"Basic dGVzdDE6dGVzdDExMTE=\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/oauth/token?grant_type=password&username=mrbird&password=123456&scope=all\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"oauth\",\n\t\t\t\t\t\t\"token\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"grant_type\",\n\t\t\t\t\t\t\t\"value\": \"password\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"username\",\n\t\t\t\t\t\t\t\"value\": \"mrbird\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"password\",\n\t\t\t\t\t\t\t\"value\": \"123456\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"scope\",\n\t\t\t\t\t\t\t\"value\": \"all\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/oauth/token 授权码模式\",\n\t\t\t\"request\": {\n\t\t\t\t\"auth\": {\n\t\t\t\t\t\"type\": \"noauth\"\n\t\t\t\t},\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\"value\": \"Basic dGVzdDE6dGVzdDExMTE=\",\n\t\t\t\t\t\t\"warning\": \"This is a duplicate header and will be overridden by the Authorization header generated by Postman.\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/oauth/token?grant_type=authorization_code&code=1qcUlU&client_id=test&redirect_uri=http://mrbird.cc&scope=all\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"oauth\",\n\t\t\t\t\t\t\"token\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"grant_type\",\n\t\t\t\t\t\t\t\"value\": \"authorization_code\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"code\",\n\t\t\t\t\t\t\t\"value\": \"1qcUlU\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"client_id\",\n\t\t\t\t\t\t\t\"value\": \"test\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"redirect_uri\",\n\t\t\t\t\t\t\t\"value\": \"http://mrbird.cc\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"scope\",\n\t\t\t\t\t\t\t\"value\": \"all\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/oauth/token 刷新令牌\",\n\t\t\t\"request\": {\n\t\t\t\t\"auth\": {\n\t\t\t\t\t\"type\": \"noauth\"\n\t\t\t\t},\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"value\": \"dGVzdDE6dGVzdDExMTE=\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJtcmJpcmQiLCJzY29wZSI6W10sImF0aSI6IjQ1NmNlZDZjLTgzMTYtNDgyNy1iY2EwLWU4ZjVlMzkyYTcyZCIsImV4cCI6MTU2MjQwMTUzMywibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNGYyOTAxMTItZDllYi00ZjQ5LTk0ZDctZmE3ODRlNmEyYWI1IiwiY2xpZW50X2lkIjoidGVzdDEifQ.5ldS0Fn4znrnW7vAAuy1RVKjvV3H01b42om3-uyNz50\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"oauth\",\n\t\t\t\t\t\t\"token\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"grant_type\",\n\t\t\t\t\t\t\t\"value\": \"refresh_token\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"refresh_token\",\n\t\t\t\t\t\t\t\"value\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJtcmJpcmQiLCJzY29wZSI6W10sImF0aSI6IjQ1NmNlZDZjLTgzMTYtNDgyNy1iY2EwLWU4ZjVlMzkyYTcyZCIsImV4cCI6MTU2MjQwMTUzMywibWVzc2FnZSI6ImhlbGxvIHdvcmxkIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiNGYyOTAxMTItZDllYi00ZjQ5LTk0ZDctZmE3ODRlNmEyYWI1IiwiY2xpZW50X2lkIjoidGVzdDEifQ.5ldS0Fn4znrnW7vAAuy1RVKjvV3H01b42om3-uyNz50\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"client_id\",\n\t\t\t\t\t\t\t\"value\": \"test1\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"client_secret\",\n\t\t\t\t\t\t\t\"value\": \"test1111\",\n\t\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/index\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"value\": \"bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjE1NTc2MjEsInVzZXJfbmFtZSI6Im1yYmlyZCIsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6ImRiYzc2NjE4LTEyMzQtNDI4My1iN2E5LWRiNjE0MGJkOWY0NCIsImNsaWVudF9pZCI6InRlc3QxIiwic2NvcGUiOlsiYWxsIl19.jpV34FhG0bkry2BY0JS8Jga7XOZ8UbBWDZ-ROoHc5ps\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/index\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"index\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/login 自定义登录获取令牌（用户名密码）\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\"value\": \"Basic dGVzdDE6dGVzdDExMTE=\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Content-Type\",\n\t\t\t\t\t\t\"type\": \"text\",\n\t\t\t\t\t\t\"value\": \"\",\n\t\t\t\t\t\t\"disabled\": true\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/login?username=mrbird&password=123456\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"login\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"username\",\n\t\t\t\t\t\t\t\"value\": \"mrbird\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"password\",\n\t\t\t\t\t\t\t\"value\": \"123456\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/code/sms\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"GET\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"deviceId\",\n\t\t\t\t\t\t\"value\": \"xxooxx\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/code/sms?mobile=17720202020\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"code\",\n\t\t\t\t\t\t\"sms\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"mobile\",\n\t\t\t\t\t\t\t\"value\": \"17720202020\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t},\n\t\t{\n\t\t\t\"name\": \"/login/mobile 手机验证码获取令牌\",\n\t\t\t\"request\": {\n\t\t\t\t\"method\": \"POST\",\n\t\t\t\t\"header\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"Authorization\",\n\t\t\t\t\t\t\"value\": \"Basic dGVzdDp0ZXN0MTIzNA==\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"key\": \"deviceId\",\n\t\t\t\t\t\t\"value\": \"xxooxx\",\n\t\t\t\t\t\t\"type\": \"text\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"url\": {\n\t\t\t\t\t\"raw\": \"localhost:8080/login/mobile?mobile=17720202020&smsCode=486498\",\n\t\t\t\t\t\"host\": [\n\t\t\t\t\t\t\"localhost\"\n\t\t\t\t\t],\n\t\t\t\t\t\"port\": \"8080\",\n\t\t\t\t\t\"path\": [\n\t\t\t\t\t\t\"login\",\n\t\t\t\t\t\t\"mobile\"\n\t\t\t\t\t],\n\t\t\t\t\t\"query\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"mobile\",\n\t\t\t\t\t\t\t\"value\": \"17720202020\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"key\": \"smsCode\",\n\t\t\t\t\t\t\t\"value\": \"486498\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"response\": []\n\t\t}\n\t]\n}"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/SecurityApplication.java",
    "content": "package cc.mrbird.security;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SecurityApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SecurityApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/config/AuthorizationServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.provider.token.TokenEnhancer;\nimport org.springframework.security.oauth2.provider.token.TokenEnhancerChain;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableAuthorizationServer\npublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    // @Autowired\n    // private TokenStore redisTokenStore;\n    @Autowired\n    private TokenStore jwtTokenStore;\n    @Autowired\n    private JwtAccessTokenConverter jwtAccessTokenConverter;\n    @Autowired\n    private AuthenticationManager authenticationManager;\n    @Autowired\n    private TokenEnhancer tokenEnhancer;\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {\n        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();\n        List<TokenEnhancer> enhancers = new ArrayList<>();\n        enhancers.add(tokenEnhancer);\n        enhancers.add(jwtAccessTokenConverter);\n        enhancerChain.setTokenEnhancers(enhancers);\n        endpoints.authenticationManager(authenticationManager)\n                .tokenStore(jwtTokenStore)\n                .accessTokenConverter(jwtAccessTokenConverter)\n                .userDetailsService(userDetailService);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"test1\")\n                .secret(new BCryptPasswordEncoder().encode(\"test1111\"))\n                .authorizedGrantTypes(\"password\", \"refresh_token\")\n                .accessTokenValiditySeconds(3600)\n                .refreshTokenValiditySeconds(864000)\n                .scopes(\"all\", \"a\", \"b\", \"c\")\n            .and()\n                .withClient(\"test2\")\n                .secret(new BCryptPasswordEncoder().encode(\"test2222\"))\n                .accessTokenValiditySeconds(7200);\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/config/JWTokenConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport cc.mrbird.security.enhancer.JWTokenEnhancer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.provider.token.TokenEnhancer;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;\nimport org.springframework.security.oauth2.provider.token.store.JwtTokenStore;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class JWTokenConfig {\n\n    @Bean\n    public TokenStore jwtTokenStore() {\n        return new JwtTokenStore(jwtAccessTokenConverter());\n    }\n\n    @Bean\n    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();\n        accessTokenConverter.setSigningKey(\"test_key\"); // 签名密钥\n        return accessTokenConverter;\n    }\n\n    @Bean\n    public TokenEnhancer tokenEnhancer() {\n        return new JWTokenEnhancer();\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/config/ResourceServerConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport cc.mrbird.security.handler.MyAuthenticationFailureHandler;\nimport cc.mrbird.security.handler.MyAuthenticationSucessHandler;\nimport cc.mrbird.security.validate.smscode.SmsAuthenticationConfig;\nimport cc.mrbird.security.validate.smscode.SmsCodeFilter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableResourceServer\npublic class ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Autowired\n    private MyAuthenticationSucessHandler authenticationSucessHandler;\n    @Autowired\n    private MyAuthenticationFailureHandler authenticationFailureHandler;\n    @Autowired\n    private SmsCodeFilter smsCodeFilter;\n    @Autowired\n    private SmsAuthenticationConfig smsAuthenticationConfig;\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加短信验证码校验过滤器\n                .formLogin() // 表单登录\n                .loginProcessingUrl(\"/login\") // 处理表单登录 URL\n                .successHandler(authenticationSucessHandler) // 处理登录成功\n                .failureHandler(authenticationFailureHandler) // 处理登录失败\n            .and()\n                .authorizeRequests() // 授权配置\n                .antMatchers(\"/code/sms\").permitAll()\n                .anyRequest()  // 所有请求\n                .authenticated() // 都需要认证\n            .and()\n                .csrf().disable()\n            .apply(smsAuthenticationConfig);\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/config/SecurityConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    @Override\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/config/TokenStoreConfig.java",
    "content": "package cc.mrbird.security.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class TokenStoreConfig {\n\n    @Autowired\n    private RedisConnectionFactory redisConnectionFactory;\n\n    // @Bean\n    // public TokenStore redisTokenStore(){\n    //     return new RedisTokenStore(redisConnectionFactory);\n    // }\n\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/controller/UserController.java",
    "content": "package cc.mrbird.security.controller;\n\nimport io.jsonwebtoken.Jwts;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.annotation.AuthenticationPrincipal;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class UserController {\n\n    @GetMapping(\"index\")\n    public Object index(@AuthenticationPrincipal Authentication authentication, HttpServletRequest request) {\n        String header = request.getHeader(\"Authorization\");\n        String token = StringUtils.substringAfter(header, \"bearer \");\n\n        return Jwts.parser().setSigningKey(\"test_key\".getBytes(StandardCharsets.UTF_8)).parseClaimsJws(token).getBody();\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/controller/ValidateController.java",
    "content": "package cc.mrbird.security.controller;\n\nimport cc.mrbird.security.service.RedisCodeService;\nimport cc.mrbird.security.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.RandomStringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@RestController\npublic class ValidateController {\n\n    @Autowired\n    private RedisCodeService redisCodeService;\n\n    @GetMapping(\"/code/sms\")\n    public void createSmsCode(HttpServletRequest request, HttpServletResponse response, String mobile) throws Exception {\n        SmsCode smsCode = createSMSCode();\n        redisCodeService.save(smsCode, new ServletWebRequest(request), mobile);\n        // 输出验证码到控制台代替短信发送服务\n        System.out.println(\"手机号\" + mobile + \"的登录验证码为：\" + smsCode.getCode() + \"，有效时间为120秒\");\n    }\n\n    private SmsCode createSMSCode() {\n        String code = RandomStringUtils.randomNumeric(6);\n        return new SmsCode(code);\n    }\n\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/domain/MyUser.java",
    "content": "package cc.mrbird.security.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/enhancer/JWTokenEnhancer.java",
    "content": "package cc.mrbird.security.enhancer;\n\nimport org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.security.oauth2.provider.OAuth2Authentication;\nimport org.springframework.security.oauth2.provider.token.TokenEnhancer;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\npublic class JWTokenEnhancer implements TokenEnhancer {\n    @Override\n    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {\n        Map<String, Object> info = new HashMap<>();\n        info.put(\"message\", \"hello world\");\n\n        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);\n        return oAuth2AccessToken;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/handler/MyAuthenticationFailureHandler.java",
    "content": "package cc.mrbird.security.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {\n\n    @Autowired\n    private ObjectMapper mapper;\n\n    @Override\n    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,\n                                        AuthenticationException exception) throws IOException {\n        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());\n        response.setContentType(\"application/json;charset=utf-8\");\n        response.getWriter().write(mapper.writeValueAsString(exception.getMessage()));\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/handler/MyAuthenticationSucessHandler.java",
    "content": "package cc.mrbird.security.handler;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;\nimport org.springframework.security.oauth2.provider.*;\nimport org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Base64;\nimport java.util.HashMap;\n\n@Component\npublic class MyAuthenticationSucessHandler implements AuthenticationSuccessHandler {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private ClientDetailsService clientDetailsService;\n    @Autowired\n    private AuthorizationServerTokenServices authorizationServerTokenServices;\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {\n        // 1. 从请求头中获取 ClientId\n        String header = request.getHeader(\"Authorization\");\n        if (header == null || !header.startsWith(\"Basic \")) {\n            throw new UnapprovedClientAuthenticationException(\"请求头中无client信息\");\n        }\n\n        String[] tokens = this.extractAndDecodeHeader(header, request);\n        String clientId = tokens[0];\n        String clientSecret = tokens[1];\n\n        TokenRequest tokenRequest = null;\n\n        // 2. 通过 ClientDetailsService 获取 ClientDetails\n        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);\n\n        // 3. 校验 ClientId和 ClientSecret的正确性\n        if (clientDetails == null) {\n            throw new UnapprovedClientAuthenticationException(\"clientId:\" + clientId + \"对应的信息不存在\");\n        } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {\n            throw new UnapprovedClientAuthenticationException(\"clientSecret不正确\");\n        } else {\n            // 4. 通过 TokenRequest构造器生成 TokenRequest\n            tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), \"custom\");\n        }\n\n        // 5. 通过 TokenRequest的 createOAuth2Request方法获取 OAuth2Request\n        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);\n        // 6. 通过 Authentication和 OAuth2Request构造出 OAuth2Authentication\n        OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);\n\n        // 7. 通过 AuthorizationServerTokenServices 生成 OAuth2AccessToken\n        OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(auth2Authentication);\n\n        // 8. 返回 Token\n        log.info(\"登录成功\");\n        response.setContentType(\"application/json;charset=UTF-8\");\n        response.getWriter().write(new ObjectMapper().writeValueAsString(token));\n    }\n\n    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) {\n        byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);\n\n        byte[] decoded;\n        try {\n            decoded = Base64.getDecoder().decode(base64Token);\n        } catch (IllegalArgumentException var7) {\n            throw new BadCredentialsException(\"Failed to decode basic authentication token\");\n        }\n\n        String token = new String(decoded, StandardCharsets.UTF_8);\n        int delim = token.indexOf(\":\");\n        if (delim == -1) {\n            throw new BadCredentialsException(\"Invalid basic authentication token\");\n        } else {\n            return new String[]{token.substring(0, delim), token.substring(delim + 1)};\n        }\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/service/RedisCodeService.java",
    "content": "package cc.mrbird.security.service;\n\nimport cc.mrbird.security.validate.smscode.SmsCode;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.context.request.ServletWebRequest;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Redis操作验证码服务\n */\n@Service\npublic class RedisCodeService {\n\n    private final static String SMS_CODE_PREFIX = \"SMS_CODE:\";\n    private final static Integer TIME_OUT = 300;\n\n    @Autowired\n    private StringRedisTemplate redisTemplate;\n\n    /**\n     * 保存验证码到 redis\n     *\n     * @param smsCode 短信验证码\n     * @param request ServletWebRequest\n     */\n    public void save(SmsCode smsCode, ServletWebRequest request, String mobile) throws Exception {\n        redisTemplate.opsForValue().set(key(request, mobile), smsCode.getCode(), TIME_OUT, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 获取验证码\n     *\n     * @param request ServletWebRequest\n     * @return 验证码\n     */\n    public String get(ServletWebRequest request, String mobile) throws Exception {\n        return redisTemplate.opsForValue().get(key(request, mobile));\n    }\n\n    /**\n     * 移除验证码\n     *\n     * @param request ServletWebRequest\n     */\n    public void remove(ServletWebRequest request, String mobile) throws Exception {\n        redisTemplate.delete(key(request, mobile));\n    }\n\n    private String key(ServletWebRequest request, String mobile) throws Exception {\n        String deviceId = request.getHeader(\"deviceId\");\n        if (StringUtils.isBlank(deviceId)) {\n            throw new Exception(\"请在请求头中设置deviceId\");\n        }\n        return SMS_CODE_PREFIX + deviceId + \":\" + mobile;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/service/UserDetailService.java",
    "content": "package cc.mrbird.security.service;\n\nimport cc.mrbird.security.domain.MyUser;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 模拟一个用户，替代数据库获取逻辑\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n        // 输出加密后的密码\n        System.out.println(user.getPassword());\n\n        List<GrantedAuthority> authorities = new ArrayList<>();\n        if (StringUtils.equalsIgnoreCase(\"mrbird\", username)) {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"admin\");\n        } else {\n            authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(\"test\");\n        }\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), authorities);\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationConfig.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.SecurityConfigurerAdapter;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.web.DefaultSecurityFilterChain;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.security.web.authentication.AuthenticationSuccessHandler;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class SmsAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {\n\n    @Autowired\n    private AuthenticationSuccessHandler authenticationSuccessHandler;\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Override\n    public void configure(HttpSecurity http) {\n        SmsAuthenticationFilter smsAuthenticationFilter = new SmsAuthenticationFilter();\n        smsAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));\n        smsAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);\n        smsAuthenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler);\n\n        SmsAuthenticationProvider smsAuthenticationProvider = new SmsAuthenticationProvider();\n        smsAuthenticationProvider.setUserDetailService(userDetailService);\n\n        http.authenticationProvider(smsAuthenticationProvider)\n                .addFilterAfter(smsAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationFilter.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;\nimport org.springframework.security.web.util.matcher.AntPathRequestMatcher;\nimport org.springframework.util.Assert;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {\n\n    public static final String MOBILE_KEY = \"mobile\";\n\n    private String mobileParameter = MOBILE_KEY;\n    private boolean postOnly = true;\n\n\n    public SmsAuthenticationFilter() {\n        super(new AntPathRequestMatcher(\"/login/mobile\", \"POST\"));\n    }\n\n\n    public Authentication attemptAuthentication(HttpServletRequest request,\n                                                HttpServletResponse response) throws AuthenticationException {\n        if (postOnly && !request.getMethod().equals(\"POST\")) {\n            throw new AuthenticationServiceException(\n                    \"Authentication method not supported: \" + request.getMethod());\n        }\n\n        String mobile = obtainMobile(request);\n\n        if (mobile == null) {\n            mobile = \"\";\n        }\n\n        mobile = mobile.trim();\n\n        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile);\n\n        setDetails(request, authRequest);\n\n        return this.getAuthenticationManager().authenticate(authRequest);\n    }\n\n    protected String obtainMobile(HttpServletRequest request) {\n        return request.getParameter(mobileParameter);\n    }\n\n    protected void setDetails(HttpServletRequest request,\n                              SmsAuthenticationToken authRequest) {\n        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));\n    }\n\n    public void setMobileParameter(String mobileParameter) {\n        Assert.hasText(mobileParameter, \"mobile parameter must not be empty or null\");\n        this.mobileParameter = mobileParameter;\n    }\n\n    public void setPostOnly(boolean postOnly) {\n        this.postOnly = postOnly;\n    }\n\n    public final String getMobileParameter() {\n        return mobileParameter;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationProvider.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.UserDetailService;\nimport org.springframework.security.authentication.AuthenticationProvider;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.core.userdetails.UserDetails;\n\npublic class SmsAuthenticationProvider implements AuthenticationProvider {\n\n    private UserDetailService userDetailService;\n\n    @Override\n    public Authentication authenticate(Authentication authentication) throws AuthenticationException {\n        SmsAuthenticationToken authenticationToken = (SmsAuthenticationToken) authentication;\n        UserDetails userDetails = userDetailService.loadUserByUsername((String) authenticationToken.getPrincipal());\n\n        if (userDetails == null)\n            throw new InternalAuthenticationServiceException(\"未找到与该手机号对应的用户\");\n\n        SmsAuthenticationToken authenticationResult = new SmsAuthenticationToken(userDetails, userDetails.getAuthorities());\n\n        authenticationResult.setDetails(authenticationToken.getDetails());\n\n        return authenticationResult;\n    }\n\n    @Override\n    public boolean supports(Class<?> aClass) {\n        return SmsAuthenticationToken.class.isAssignableFrom(aClass);\n    }\n\n    public UserDetailService getUserDetailService() {\n        return userDetailService;\n    }\n\n    public void setUserDetailService(UserDetailService userDetailService) {\n        this.userDetailService = userDetailService;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsAuthenticationToken.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport org.springframework.security.authentication.AbstractAuthenticationToken;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.SpringSecurityCoreVersion;\n\nimport java.util.Collection;\n\npublic class SmsAuthenticationToken extends AbstractAuthenticationToken {\n\n    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;\n\n    private final Object principal;\n\n    public SmsAuthenticationToken(String mobile) {\n        super(null);\n        this.principal = mobile;\n        setAuthenticated(false);\n    }\n\n    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {\n        super(authorities);\n        this.principal = principal;\n        super.setAuthenticated(true); // must use super, as we override\n    }\n\n    @Override\n    public Object getCredentials() {\n        return null;\n    }\n\n    public Object getPrincipal() {\n        return this.principal;\n    }\n\n    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {\n        if (isAuthenticated) {\n            throw new IllegalArgumentException(\n                    \"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead\");\n        }\n\n        super.setAuthenticated(false);\n    }\n\n    @Override\n    public void eraseCredentials() {\n        super.eraseCredentials();\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsCode.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\npublic class SmsCode {\n\n    private String code;\n\n    public SmsCode(String code) {\n        this.code = code;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/java/cc/mrbird/security/validate/smscode/SmsCodeFilter.java",
    "content": "package cc.mrbird.security.validate.smscode;\n\nimport cc.mrbird.security.service.RedisCodeService;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationServiceException;\nimport org.springframework.security.web.authentication.AuthenticationFailureHandler;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.ServletRequestUtils;\nimport org.springframework.web.context.request.ServletWebRequest;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@Component\npublic class SmsCodeFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private AuthenticationFailureHandler authenticationFailureHandler;\n    @Autowired\n    private RedisCodeService redisCodeService;\n\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {\n        if (StringUtils.equalsIgnoreCase(\"/login/mobile\", httpServletRequest.getRequestURI())\n                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), \"post\")) {\n            try {\n                validateCode(new ServletWebRequest(httpServletRequest));\n            } catch (Exception e) {\n                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, new AuthenticationServiceException(e.getMessage()));\n                return;\n            }\n        }\n        filterChain.doFilter(httpServletRequest, httpServletResponse);\n    }\n\n    private void validateCode(ServletWebRequest servletWebRequest) throws Exception {\n        String smsCodeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"smsCode\");\n        String mobileInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), \"mobile\");\n\n        String codeInRedis = redisCodeService.get(servletWebRequest, mobileInRequest);\n\n        if (StringUtils.isBlank(smsCodeInRequest)) {\n            throw new Exception(\"验证码不能为空！\");\n        }\n        if (codeInRedis == null) {\n            throw new Exception(\"验证码已过期！\");\n        }\n        if (!StringUtils.equalsIgnoreCase(codeInRedis, smsCodeInRequest)) {\n            throw new Exception(\"验证码不正确！\");\n        }\n        redisCodeService.remove(servletWebRequest, mobileInRequest);\n\n    }\n}"
  },
  {
    "path": "65.Spring-Security-OAuth2-Config/src/main/resources/application.yml",
    "content": "security:\n  oauth2:\n    client:\n      client-id: test\n      client-secret: test1234\n\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>pom</packaging>\n    <modules>\n        <module>sso-application-one</module>\n        <module>sso-application-two</module>\n        <module>sso-server</module>\n    </modules>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.6.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>sso</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>sso</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-oauth2</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-security</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sso</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sso-application-one</artifactId>\n\n\n</project>"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/src/main/java/cc/mrbird/sso/SsoApplicaitonOne.java",
    "content": "package cc.mrbird.sso;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n/**\n * @author MrBird\n */\n@EnableOAuth2Sso\n@SpringBootApplication\npublic class SsoApplicaitonOne {\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(SsoApplicaitonOne.class).run(args);\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/src/main/java/cc/mrbird/sso/client/config/WebSecurityConfigurer.java",
    "content": "package cc.mrbird.sso.client.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n/**\n * @author MrBird\n */\n@Order(101)\n@Configuration\n@EnableGlobalMethodSecurity(prePostEnabled = true)\npublic class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/src/main/java/cc/mrbird/sso/client/controller/UserController.java",
    "content": "package cc.mrbird.sso.client.controller;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.security.Principal;;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class UserController {\n\n    @GetMapping(\"user\")\n    public Principal user(Principal principal) {\n        return principal;\n    }\n\n    @GetMapping(\"auth/test1\")\n    @PreAuthorize(\"hasAuthority('user:add')\")\n    public String authTest1(){\n        return \"您拥有'user:add'权限\";\n    }\n\n    @GetMapping(\"auth/test2\")\n    @PreAuthorize(\"hasAuthority('user:update')\")\n    public String authTest2(){\n        return \"您拥有'user:update'权限\";\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/src/main/resources/application.yml",
    "content": "security:\n  oauth2:\n    client:\n      client-id: app-a\n      client-secret: app-a-1234\n      user-authorization-uri: http://127.0.0.1:8080/server/oauth/authorize\n      access-token-uri: http://127.0.0.1:8080/server/oauth/token\n    resource:\n      jwt:\n        key-uri: http://127.0.0.1:8080/server/oauth/token_key\nserver:\n  port: 9090\n  servlet:\n    context-path: /app1"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-one/src/main/resources/static/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>管理系统一</title>\n</head>\n<body>\n    <h1>管理系统一</h1>\n    <a href=\"http://127.0.0.1:9091/app2/index.html\">跳转到管理系统二</a>\n</body>\n</html>"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-two/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sso</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sso-application-two</artifactId>\n\n\n</project>"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-two/src/main/java/cc/mrbird/sso/SsoApplicaitonTwo.java",
    "content": "package cc.mrbird.sso;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n/**\n * @author MrBird\n */\n@EnableOAuth2Sso\n@SpringBootApplication\npublic class SsoApplicaitonTwo {\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(SsoApplicaitonTwo.class).run(args);\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-two/src/main/java/cc/mrbird/sso/client/controller/UserController.java",
    "content": "package cc.mrbird.sso.client.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class UserController {\n\n    @GetMapping(\"user\")\n    public Authentication user(Authentication authentication) {\n        return authentication;\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-two/src/main/resources/application.yml",
    "content": "security:\n  oauth2:\n    client:\n      client-id: app-b\n      client-secret: app-b-1234\n      user-authorization-uri: http://127.0.0.1:8080/server/oauth/authorize\n      access-token-uri: http://127.0.0.1:8080/server/oauth/token\n    resource:\n      jwt:\n        key-uri: http://127.0.0.1:8080/server/oauth/token_key\nserver:\n  port: 9091\n  servlet:\n    context-path: /app2"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-application-two/src/main/resources/static/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>管理系统二</title>\n</head>\n<body>\n<h1>管理系统二</h1>\n<a href=\"http://127.0.0.1:9090/app1/index.html\">跳转到管理系统一</a>\n</body>\n</html>"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>sso</artifactId>\n        <groupId>cc.mrbird</groupId>\n        <version>0.0.1-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>sso-server</artifactId>\n</project>"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/java/cc/mrbird/sso/SsoServerApplication.java",
    "content": "package cc.mrbird.sso;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.builder.SpringApplicationBuilder;\n\n/**\n * @author MrBird\n */\n@SpringBootApplication\npublic class SsoServerApplication {\n\n    public static void main(String[] args) {\n        new SpringApplicationBuilder(SsoServerApplication.class).run(args);\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/java/cc/mrbird/sso/server/config/SsoAuthorizationServerConfig.java",
    "content": "package cc.mrbird.sso.server.config;\n\nimport cc.mrbird.sso.server.service.UserDetailService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;\nimport org.springframework.security.oauth2.provider.token.store.JwtTokenStore;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableAuthorizationServer\npublic class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n    @Autowired\n    private UserDetailService userDetailService;\n\n    @Bean\n    public TokenStore jwtTokenStore() {\n        return new JwtTokenStore(jwtAccessTokenConverter());\n    }\n\n    @Bean\n    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();\n        accessTokenConverter.setSigningKey(\"test_key\");\n        return accessTokenConverter;\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"app-a\")\n                .secret(passwordEncoder.encode(\"app-a-1234\"))\n                .authorizedGrantTypes(\"refresh_token\",\"authorization_code\")\n                .accessTokenValiditySeconds(3600)\n                .scopes(\"all\")\n                .autoApprove(true)\n                .redirectUris(\"http://127.0.0.1:9090/app1/login\")\n            .and()\n                .withClient(\"app-b\")\n                .secret(passwordEncoder.encode(\"app-b-1234\"))\n                .authorizedGrantTypes(\"refresh_token\",\"authorization_code\")\n                .accessTokenValiditySeconds(7200)\n                .scopes(\"all\")\n                .autoApprove(true)\n                .redirectUris(\"http://127.0.0.1:9091/app2/login\");\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {\n        endpoints.tokenStore(jwtTokenStore())\n                .accessTokenConverter(jwtAccessTokenConverter())\n                .userDetailsService(userDetailService);\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer security) {\n        security.tokenKeyAccess(\"isAuthenticated()\"); // 获取密钥需要身份认证\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/java/cc/mrbird/sso/server/config/WebSecurityConfig.java",
    "content": "package cc.mrbird.sso.server.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.formLogin()\n            .and()\n                .authorizeRequests()\n                .anyRequest()\n                .authenticated();\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/java/cc/mrbird/sso/server/domain/MyUser.java",
    "content": "package cc.mrbird.sso.server.domain;\n\nimport java.io.Serializable;\n\npublic class MyUser implements Serializable {\n    private static final long serialVersionUID = 3497935890426858541L;\n\n    private String userName;\n\n    private String password;\n\n    private boolean accountNonExpired = true;\n\n    private boolean accountNonLocked= true;\n\n    private boolean credentialsNonExpired= true;\n\n    private boolean enabled= true;\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public boolean isAccountNonExpired() {\n        return accountNonExpired;\n    }\n\n    public void setAccountNonExpired(boolean accountNonExpired) {\n        this.accountNonExpired = accountNonExpired;\n    }\n\n    public boolean isAccountNonLocked() {\n        return accountNonLocked;\n    }\n\n    public void setAccountNonLocked(boolean accountNonLocked) {\n        this.accountNonLocked = accountNonLocked;\n    }\n\n    public boolean isCredentialsNonExpired() {\n        return credentialsNonExpired;\n    }\n\n    public void setCredentialsNonExpired(boolean credentialsNonExpired) {\n        this.credentialsNonExpired = credentialsNonExpired;\n    }\n\n    public boolean isEnabled() {\n        return enabled;\n    }\n\n    public void setEnabled(boolean enabled) {\n        this.enabled = enabled;\n    }\n}\n"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/java/cc/mrbird/sso/server/service/UserDetailService.java",
    "content": "package cc.mrbird.sso.server.service;\n\nimport cc.mrbird.sso.server.domain.MyUser;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.core.authority.AuthorityUtils;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.security.crypto.password.PasswordEncoder;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class UserDetailService implements UserDetailsService {\n\n    @Autowired\n    private PasswordEncoder passwordEncoder;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        MyUser user = new MyUser();\n        user.setUserName(username);\n        user.setPassword(this.passwordEncoder.encode(\"123456\"));\n\n        return new User(username, user.getPassword(), user.isEnabled(),\n                user.isAccountNonExpired(), user.isCredentialsNonExpired(),\n                user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList(\"user:add\"));\n    }\n}"
  },
  {
    "path": "66.Spring-Security-OAuth2-SSO/sso-server/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\n  servlet:\n    context-path: /server\n"
  },
  {
    "path": "67.spring-batch-start/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-start</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-start</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/SpringBatchStartApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchStartApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchStartApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/decider/MyDecider.java",
    "content": "package cc.mrbird.batch.decider;\n\nimport org.springframework.batch.core.JobExecution;\nimport org.springframework.batch.core.StepExecution;\nimport org.springframework.batch.core.job.flow.FlowExecutionStatus;\nimport org.springframework.batch.core.job.flow.JobExecutionDecider;\nimport org.springframework.stereotype.Component;\n\nimport java.time.DayOfWeek;\nimport java.time.LocalDate;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyDecider implements JobExecutionDecider {\n    @Override\n    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {\n        LocalDate now = LocalDate.now();\n        DayOfWeek dayOfWeek = now.getDayOfWeek();\n\n        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {\n            return new FlowExecutionStatus(\"weekend\");\n        } else {\n            return new FlowExecutionStatus(\"workingDay\");\n        }\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/DeciderJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.decider.MyDecider;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class DeciderJobDemo {\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private MyDecider myDecider;\n\n    @Bean\n    public Job deciderJob() {\n        return jobBuilderFactory.get(\"deciderJob\")\n                .start(step1())\n                .next(myDecider)\n                .from(myDecider).on(\"weekend\").to(step2())\n                .from(myDecider).on(\"workingDay\").to(step3())\n                .from(step3()).on(\"*\").to(step4())\n                .end()\n                .build();\n    }\n\n    private Step step1() {\n        return stepBuilderFactory.get(\"step1\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤一操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step2() {\n        return stepBuilderFactory.get(\"step2\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤二操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step3() {\n        return stepBuilderFactory.get(\"step3\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤三操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n\n    private Step step4() {\n        return stepBuilderFactory.get(\"step4\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤四操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/FirstJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class FirstJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job firstJob() {\n        return jobBuilderFactory.get(\"firstJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .tasklet((contribution, chunkContext) -> {\n                    System.out.println(\"执行步骤....\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/FlowJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.ExitStatus;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.core.job.builder.FlowBuilder;\nimport org.springframework.batch.core.job.flow.Flow;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class FlowJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job flowJob() {\n        return jobBuilderFactory.get(\"flowJob\")\n                .start(flow())\n                .next(step3())\n                .end()\n                .build();\n    }\n\n    private Step step1() {\n        return stepBuilderFactory.get(\"step1\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤一操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step2() {\n        return stepBuilderFactory.get(\"step2\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤二操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step3() {\n        return stepBuilderFactory.get(\"step3\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤三操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    // 创建一个flow对象，包含若干个step\n    private Flow flow() {\n        return new FlowBuilder<Flow>(\"flow\")\n                .start(step1())\n                .next(step2())\n                .build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/MultiStepJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.ExitStatus;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MultiStepJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job multiStepJob() {\n        // return jobBuilderFactory.get(\"multiStepJob\")\n        //         .start(step1())\n        //         .next(step2())\n        //         .next(step3())\n        //         .build();\n        return jobBuilderFactory.get(\"multiStepJob2\")\n                .start(step1())\n                .on(ExitStatus.COMPLETED.getExitCode()).to(step2())\n                .from(step2())\n                .on(ExitStatus.COMPLETED.getExitCode()).to(step3())\n                .from(step3()).end()\n                .build();\n    }\n\n    private Step step1() {\n        return stepBuilderFactory.get(\"step1\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤一操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step2() {\n        return stepBuilderFactory.get(\"step2\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤二操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step3() {\n        return stepBuilderFactory.get(\"step3\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤三操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/NestedJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.core.launch.JobLauncher;\nimport org.springframework.batch.core.repository.JobRepository;\nimport org.springframework.batch.core.step.builder.JobStepBuilder;\nimport org.springframework.batch.core.step.builder.StepBuilder;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.PlatformTransactionManager;\n\n/**\n * @author MrBird\n */\n@Component\npublic class NestedJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private JobLauncher jobLauncher;\n    @Autowired\n    private JobRepository jobRepository;\n    @Autowired\n    private PlatformTransactionManager platformTransactionManager;\n\n    // 父任务\n    @Bean\n    public Job parentJob() {\n        return jobBuilderFactory.get(\"parentJob\")\n                .start(childJobOneStep())\n                .next(childJobTwoStep())\n                .build();\n    }\n\n\n    // 将任务转换为特殊的步骤\n    private Step childJobOneStep() {\n        return new JobStepBuilder(new StepBuilder(\"childJobOneStep\"))\n                .job(childJobOne())\n                .launcher(jobLauncher)\n                .repository(jobRepository)\n                .transactionManager(platformTransactionManager)\n                .build();\n    }\n\n    // 将任务转换为特殊的步骤\n    private Step childJobTwoStep() {\n        return new JobStepBuilder(new StepBuilder(\"childJobTwoStep\"))\n                .job(childJobTwo())\n                .launcher(jobLauncher)\n                .repository(jobRepository)\n                .transactionManager(platformTransactionManager)\n                .build();\n    }\n\n    // 子任务一\n    private Job childJobOne() {\n        return jobBuilderFactory.get(\"childJobOne\")\n                .start(\n                    stepBuilderFactory.get(\"childJobOneStep\")\n                            .tasklet((stepContribution, chunkContext) -> {\n                                System.out.println(\"子任务一执行步骤。。。\");\n                                return RepeatStatus.FINISHED;\n                            }).build()\n                ).build();\n    }\n\n    // 子任务二\n    private Job childJobTwo() {\n        return jobBuilderFactory.get(\"childJobTwo\")\n                .start(\n                    stepBuilderFactory.get(\"childJobTwoStep\")\n                            .tasklet((stepContribution, chunkContext) -> {\n                                System.out.println(\"子任务二执行步骤。。。\");\n                                return RepeatStatus.FINISHED;\n                            }).build()\n                ).build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/java/cc/mrbird/batch/job/SplitJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.core.job.builder.FlowBuilder;\nimport org.springframework.batch.core.job.flow.Flow;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.task.SimpleAsyncTaskExecutor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class SplitJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job splitJob() {\n        return jobBuilderFactory.get(\"splitJob\")\n                .start(flow1())\n                .split(new SimpleAsyncTaskExecutor()).add(flow2())\n                .end()\n                .build();\n\n    }\n\n    private Step step1() {\n        return stepBuilderFactory.get(\"step1\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤一操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step2() {\n        return stepBuilderFactory.get(\"step2\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤二操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Step step3() {\n        return stepBuilderFactory.get(\"step3\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    System.out.println(\"执行步骤三操作。。。\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private Flow flow1() {\n        return new FlowBuilder<Flow>(\"flow1\")\n                .start(step1())\n                .next(step2())\n                .build();\n    }\n\n    private Flow flow2() {\n        return new FlowBuilder<Flow>(\"flow2\")\n                .start(step3())\n                .build();\n    }\n}\n"
  },
  {
    "path": "67.spring-batch-start/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456"
  },
  {
    "path": "68.spring-batch-itemreader/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-itemreader</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-itemreader</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-oxm</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.thoughtworks.xstream</groupId>\n            <artifactId>xstream</artifactId>\n            <version>1.4.11.1</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/SpringBatchItemreaderApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchItemreaderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchItemreaderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/entity/TestData.java",
    "content": "package cc.mrbird.batch.entity;\n\n/**\n * @author MrBird\n */\npublic class TestData {\n    private int id;\n    private String field1;\n    private String field2;\n    private String field3;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getField1() {\n        return field1;\n    }\n\n    public void setField1(String field1) {\n        this.field1 = field1;\n    }\n\n    public String getField2() {\n        return field2;\n    }\n\n    public void setField2(String field2) {\n        this.field2 = field2;\n    }\n\n    public String getField3() {\n        return field3;\n    }\n\n    public void setField3(String field3) {\n        this.field3 = field3;\n    }\n\n    @Override\n    public String toString() {\n        return \"TestData{\" +\n                \"id=\" + id +\n                \", field1='\" + field1 + '\\'' +\n                \", field2='\" + field2 + '\\'' +\n                \", field3='\" + field3 + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/DataSourceItemReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.batch.item.database.JdbcPagingItemReader;\nimport org.springframework.batch.item.database.Order;\nimport org.springframework.batch.item.database.support.MySqlPagingQueryProvider;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport javax.sql.DataSource;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Component\npublic class DataSourceItemReaderDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    // 注入数据源\n    @Autowired\n    private DataSource dataSource;\n\n    @Bean\n    public Job dataSourceItemReaderJob() throws Exception {\n        return jobBuilderFactory.get(\"dataSourceItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() throws Exception {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(dataSourceItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ItemReader<TestData> dataSourceItemReader() throws Exception {\n        JdbcPagingItemReader<TestData> reader = new JdbcPagingItemReader<>();\n        reader.setDataSource(dataSource); // 设置数据源\n        reader.setFetchSize(5); // 每次取多少条记录\n        reader.setPageSize(5); // 设置每页数据量\n\n        // 指定sql查询语句 select id,field1,field2,field3 from TEST\n        MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider();\n        provider.setSelectClause(\"id,field1,field2,field3\"); //设置查询字段\n        provider.setFromClause(\"from TEST\"); // 设置从哪张表查询\n\n        // 将读取到的数据转换为TestData对象\n        reader.setRowMapper((resultSet, rowNum) -> {\n            TestData data = new TestData();\n            data.setId(resultSet.getInt(1));\n            data.setField1(resultSet.getString(2)); // 读取第一个字段，类型为String\n            data.setField2(resultSet.getString(3));\n            data.setField3(resultSet.getString(4));\n            return data;\n        });\n\n        Map<String, Order> sort = new HashMap<>(1);\n        sort.put(\"id\", Order.ASCENDING);\n        provider.setSortKeys(sort); // 设置排序,通过id 升序\n\n        reader.setQueryProvider(provider);\n\n        // 设置namedParameterJdbcTemplate等属性\n        reader.afterPropertiesSet();\n        return reader;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/FileItemReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.batch.item.file.FlatFileItemReader;\nimport org.springframework.batch.item.file.mapping.DefaultLineMapper;\nimport org.springframework.batch.item.file.transform.DelimitedLineTokenizer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class FileItemReaderDemo {\n    // 任务创建工厂\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    // 步骤创建工厂\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job fileItemReaderJob() {\n        return jobBuilderFactory.get(\"fileItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(fileItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ItemReader<TestData> fileItemReader() {\n        FlatFileItemReader<TestData> reader = new FlatFileItemReader<>();\n        reader.setResource(new ClassPathResource(\"file\")); // 设置文件资源地址\n        reader.setLinesToSkip(1); // 忽略第一行\n\n        // AbstractLineTokenizer的三个实现类之一，以固定分隔符处理行数据读取,\n        // 使用默认构造器的时候，使用逗号作为分隔符，也可以通过有参构造器来指定分隔符\n        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();\n\n        // 设置属性名，类似于表头\n        tokenizer.setNames(\"id\", \"field1\", \"field2\", \"field3\");\n        // 将每行数据转换为TestData对象\n        DefaultLineMapper<TestData> mapper = new DefaultLineMapper<>();\n        mapper.setLineTokenizer(tokenizer);\n        // 设置映射方式\n        mapper.setFieldSetMapper(fieldSet -> {\n            TestData data = new TestData();\n            data.setId(fieldSet.readInt(\"id\"));\n            data.setField1(fieldSet.readString(\"field1\"));\n            data.setField2(fieldSet.readString(\"field2\"));\n            data.setField3(fieldSet.readString(\"field3\"));\n            return data;\n        });\n\n        reader.setLineMapper(mapper);\n        return reader;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/JSONFileItemReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.batch.item.json.JacksonJsonObjectReader;\nimport org.springframework.batch.item.json.JsonItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class JSONFileItemReaderDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job jsonFileItemReaderJob() {\n        return jobBuilderFactory.get(\"jsonFileItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(jsonItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ItemReader<TestData> jsonItemReader() {\n        // 设置json文件地址\n        ClassPathResource resource = new ClassPathResource(\"file.json\");\n        // 设置json文件转换的目标对象类型\n        JacksonJsonObjectReader<TestData> jacksonJsonObjectReader = new JacksonJsonObjectReader<>(TestData.class);\n        JsonItemReader<TestData> reader = new JsonItemReader<>(resource, jacksonJsonObjectReader);\n        // 给reader设置一个别名\n        reader.setName(\"testDataJsonItemReader\");\n        return reader;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/MultiFileIteamReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.batch.item.file.FlatFileItemReader;\nimport org.springframework.batch.item.file.MultiResourceItemReader;\nimport org.springframework.batch.item.file.mapping.DefaultLineMapper;\nimport org.springframework.batch.item.file.transform.DelimitedLineTokenizer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.core.io.Resource;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n *\n * 5. 演示多文件读取\n */\n@Component\npublic class MultiFileIteamReaderDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n\n    @Bean\n    public Job multiFileItemReaderJob() {\n        return jobBuilderFactory.get(\"multiFileItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(multiFileItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ItemReader<TestData> multiFileItemReader() {\n        MultiResourceItemReader<TestData> reader = new MultiResourceItemReader<>();\n        reader.setDelegate(fileItemReader()); // 设置文件读取代理，方法可以使用前面文件读取中的例子\n\n        Resource[] resources = new Resource[]{\n                new ClassPathResource(\"file1\"),\n                new ClassPathResource(\"file2\")\n        };\n\n        reader.setResources(resources); // 设置多文件源\n        return reader;\n    }\n\n    private FlatFileItemReader<TestData> fileItemReader() {\n        FlatFileItemReader<TestData> reader = new FlatFileItemReader<>();\n        reader.setLinesToSkip(1); // 忽略第一行\n\n        // AbstractLineTokenizer的三个实现类之一，以固定分隔符处理行数据读取,\n        // 使用默认构造器的时候，使用逗号作为分隔符，也可以通过有参构造器来指定分隔符\n        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();\n        // 设置属姓名，类似于表头\n        tokenizer.setNames(\"id\", \"field1\", \"field2\", \"field3\");\n        // 将每行数据转换为TestData对象\n        DefaultLineMapper<TestData> mapper = new DefaultLineMapper<>();\n        mapper.setLineTokenizer(tokenizer);\n        // 设置映射方式\n        mapper.setFieldSetMapper(fieldSet -> {\n            TestData data = new TestData();\n            data.setId(fieldSet.readInt(\"id\"));\n            data.setField1(fieldSet.readString(\"field1\"));\n            data.setField2(fieldSet.readString(\"field2\"));\n            data.setField3(fieldSet.readString(\"field3\"));\n            return data;\n        });\n\n        reader.setLineMapper(mapper);\n        return reader;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/MySimpleItemReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.reader.MySimpleIteamReader;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MySimpleItemReaderDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job mySimpleItemReaderJob() {\n        return jobBuilderFactory.get(\"mySimpleItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<String, String>chunk(2)\n                .reader(mySimpleItemReader())\n                .writer(list -> list.forEach(System.out::println))  // 简单输出，后面再详细介绍writer\n                .build();\n    }\n\n    private ItemReader<String> mySimpleItemReader() {\n        List<String> data = Arrays.asList(\"java\", \"c++\", \"javascript\", \"python\");\n        return new MySimpleIteamReader(data);\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/job/XmlFileItemReaderDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemReader;\nimport org.springframework.batch.item.xml.StaxEventItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.oxm.xstream.XStreamMarshaller;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Component\npublic class XmlFileItemReaderDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job xmlFileItemReaderJob() {\n        return jobBuilderFactory.get(\"xmlFileItemReaderJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(xmlFileItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ItemReader<TestData> xmlFileItemReader() {\n        StaxEventItemReader<TestData> reader = new StaxEventItemReader<>();\n        reader.setResource(new ClassPathResource(\"file.xml\")); // 设置xml文件源\n        reader.setFragmentRootElementName(\"test\"); // 指定xml文件的根标签\n        // 将xml数据转换为TestData对象\n        XStreamMarshaller marshaller = new XStreamMarshaller();\n        // 指定需要转换的目标数据类型\n        Map<String, Class<TestData>> map = new HashMap<>(1);\n        map.put(\"test\", TestData.class);\n        marshaller.setAliases(map);\n\n        reader.setUnmarshaller(marshaller);\n        return reader;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/java/cc/mrbird/batch/reader/MySimpleIteamReader.java",
    "content": "package cc.mrbird.batch.reader;\n\nimport org.springframework.batch.item.ItemReader;\n\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\npublic class MySimpleIteamReader implements ItemReader<String> {\n\n    private Iterator<String> iterator;\n\n    public MySimpleIteamReader(List<String> data) {\n        this.iterator = data.iterator();\n    }\n\n    @Override\n    public String read() {\n        // 数据一个接着一个读取\n        return iterator.hasNext() ? iterator.next() : null;\n    }\n}\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/TEST.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : localhost_mysql\n Source Server Type    : MySQL\n Source Server Version : 50724\n Source Host           : localhost:3306\n Source Schema         : springbatch\n\n Target Server Type    : MySQL\n Target Server Version : 50724\n File Encoding         : 65001\n\n Date: 07/03/2020 15:50:05\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for TEST\n-- ----------------------------\nDROP TABLE IF EXISTS `TEST`;\nCREATE TABLE `TEST` (\n  `id` bigint(10) NOT NULL COMMENT 'ID',\n  `field1` varchar(10) NOT NULL COMMENT '字段一',\n  `field2` varchar(10) NOT NULL COMMENT '字段二',\n  `field3` varchar(10) NOT NULL COMMENT '字段三',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\n-- ----------------------------\n-- Records of TEST\n-- ----------------------------\nBEGIN;\nINSERT INTO `TEST` VALUES (1, '11', '12', '13');\nINSERT INTO `TEST` VALUES (2, '21', '22', '23');\nINSERT INTO `TEST` VALUES (3, '31', '32', '33');\nINSERT INTO `TEST` VALUES (4, '41', '42', '43');\nINSERT INTO `TEST` VALUES (5, '51', '52', '53');\nINSERT INTO `TEST` VALUES (6, '61', '62', '63');\nCOMMIT;\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/file",
    "content": "// 演示文件数据读取\n1,11,12,13\n2,21,22,23\n3,31,32,33\n4,41,42,43\n5,51,52,53\n6,61,62,63"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/file.json",
    "content": "[\n  {\n    \"id\": 1,\n    \"field1\": \"11\",\n    \"field2\": \"12\",\n    \"field3\": \"13\"\n  },\n  {\n    \"id\": 2,\n    \"field1\": \"21\",\n    \"field2\": \"22\",\n    \"field3\": \"23\"\n  },\n  {\n    \"id\": 3,\n    \"field1\": \"31\",\n    \"field2\": \"32\",\n    \"field3\": \"33\"\n  }\n]"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/file.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<tests>\n    <test>\n        <id>1</id>\n        <field1>11</field1>\n        <field2>12</field2>\n        <field3>13</field3>\n    </test>\n    <test>\n        <id>2</id>\n        <field1>21</field1>\n        <field2>22</field2>\n        <field3>23</field3>\n    </test>\n    <test>\n        <id>3</id>\n        <field1>31</field1>\n        <field2>32</field2>\n        <field3>33</field3>\n    </test>\n    <test>\n        <id>4</id>\n        <field1>41</field1>\n        <field2>42</field2>\n        <field3>43</field3>\n    </test>\n    <test>\n        <id>5</id>\n        <field1>51</field1>\n        <field2>52</field2>\n        <field3>53</field3>\n    </test>\n    <test>\n        <id>6</id>\n        <field1>61</field1>\n        <field2>62</field2>\n        <field3>63</field3>\n    </test>\n</tests>"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/file1",
    "content": "// 演示文件数据读取\n1,11,12,13\n2,21,22,23\n3,31,32,33\n4,41,42,43\n5,51,52,53\n6,61,62,63"
  },
  {
    "path": "68.spring-batch-itemreader/src/main/resources/file2",
    "content": "// 演示文件数据读取\n7,71,72,73\n8,81,82,83"
  },
  {
    "path": "69.spring-batch-itemwriter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-itemwriter</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-itemwriter</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-oxm</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.thoughtworks.xstream</groupId>\n            <artifactId>xstream</artifactId>\n            <version>1.4.11.1</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/SpringBatchItemwriterApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchItemwriterApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchItemwriterApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/entity/TestData.java",
    "content": "package cc.mrbird.batch.entity;\n\n/**\n * @author MrBird\n */\npublic class TestData {\n\n    private int id;\n    private String field1;\n    private String field2;\n    private String field3;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getField1() {\n        return field1;\n    }\n\n    public void setField1(String field1) {\n        this.field1 = field1;\n    }\n\n    public String getField2() {\n        return field2;\n    }\n\n    public void setField2(String field2) {\n        this.field2 = field2;\n    }\n\n    public String getField3() {\n        return field3;\n    }\n\n    public void setField3(String field3) {\n        this.field3 = field3;\n    }\n\n    @Override\n    public String toString() {\n        return \"TestData{\" +\n                \"id=\" + id +\n                \", field1='\" + field1 + '\\'' +\n                \", field2='\" + field2 + '\\'' +\n                \", field3='\" + field3 + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/job/DatabaseItemWriterDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemWriter;\nimport org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;\nimport org.springframework.batch.item.database.JdbcBatchItemWriter;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport javax.sql.DataSource;\n\n/**\n * @author MrBird\n */\n@Component\npublic class DatabaseItemWriterDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n    @Autowired\n    private DataSource dataSource;\n\n    @Bean\n    public Job datasourceItemWriterJob() {\n        return jobBuilderFactory.get(\"datasourceItemWriterJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .writer(dataSourceItemWriter())\n                .build();\n    }\n\n    private ItemWriter<TestData> dataSourceItemWriter() {\n        // ItemWriter的实现类之一，mysql数据库数据写入使用JdbcBatchItemWriter，\n        // 其他实现：MongoItemWriter,Neo4jItemWriter等\n        JdbcBatchItemWriter<TestData> writer = new JdbcBatchItemWriter<>();\n        writer.setDataSource(dataSource); // 设置数据源\n\n        String sql = \"insert into TEST(id,field1,field2,field3) values (:id,:field1,:field2,:field3)\";\n        writer.setSql(sql); // 设置插入sql脚本\n\n        // 映射TestData对象属性到占位符中的属性\n        BeanPropertyItemSqlParameterSourceProvider<TestData> provider = new BeanPropertyItemSqlParameterSourceProvider<>();\n        writer.setItemSqlParameterSourceProvider(provider);\n\n        writer.afterPropertiesSet(); // 设置一些额外属性\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/job/FileItemWriterDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.file.FlatFileItemWriter;\nimport org.springframework.batch.item.file.transform.LineAggregator;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.stereotype.Component;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\n/**\n * @author MrBird\n */\n@Component\npublic class FileItemWriterDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n\n    @Bean\n    public Job fileItemWriterJob() throws Exception {\n        return jobBuilderFactory.get(\"fileItemWriterJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() throws Exception {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .writer(fileItemWriter())\n                .build();\n    }\n\n    private FlatFileItemWriter<TestData> fileItemWriter() throws Exception {\n        FlatFileItemWriter<TestData> writer = new FlatFileItemWriter<>();\n\n        FileSystemResource file = new FileSystemResource(\"/Users/mrbird/Desktop/file\");\n        Path path = Paths.get(file.getPath());\n        if (!Files.exists(path)) {\n            Files.createFile(path);\n        }\n        writer.setResource(file); // 设置目标文件路径\n\n        // 把读到的每个TestData对象转换为JSON字符串\n        LineAggregator<TestData> aggregator = item -> {\n            try {\n                ObjectMapper mapper = new ObjectMapper();\n                return mapper.writeValueAsString(item);\n            } catch (JsonProcessingException e) {\n                e.printStackTrace();\n            }\n            return \"\";\n        };\n\n        writer.setLineAggregator(aggregator);\n        writer.afterPropertiesSet();\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/job/JsonFileItemWriterDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.json.JacksonJsonObjectMarshaller;\nimport org.springframework.batch.item.json.JsonFileItemWriter;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\n\n/**\n * @author MrBird\n */\n@Component\npublic class JsonFileItemWriterDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n\n    @Bean\n    public Job jsonFileItemWriterJob() throws Exception {\n        return jobBuilderFactory.get(\"jsonFileItemWriterJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() throws Exception {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .writer(jsonFileItemWriter())\n                .build();\n    }\n\n    private JsonFileItemWriter<TestData> jsonFileItemWriter() throws IOException {\n        // 文件输出目标地址\n        FileSystemResource file = new FileSystemResource(\"/Users/mrbird/Desktop/file.json\");\n        Path path = Paths.get(file.getPath());\n        if (!Files.exists(path)) {\n            Files.createFile(path);\n        }\n        // 将对象转换为json\n        JacksonJsonObjectMarshaller<TestData> marshaller = new JacksonJsonObjectMarshaller<>();\n        JsonFileItemWriter<TestData> writer = new JsonFileItemWriter<>(file, marshaller);\n        // 设置别名\n        writer.setName(\"testDatasonFileItemWriter\");\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/job/MultiFileItemWriteDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemStreamWriter;\nimport org.springframework.batch.item.ItemWriter;\nimport org.springframework.batch.item.support.ClassifierCompositeItemWriter;\nimport org.springframework.batch.item.support.CompositeItemWriter;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.classify.Classifier;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport javax.sql.DataSource;\nimport java.util.Arrays;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MultiFileItemWriteDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n    @Autowired\n    private ItemStreamWriter<TestData> fileItemWriter;\n    @Autowired\n    private ItemStreamWriter<TestData> xmlFileItemWriter;\n\n    @Bean\n    public Job multiFileItemWriterJob() {\n        return jobBuilderFactory.get(\"multiFileItemWriterJob6\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .writer(multiFileItemWriter())\n                // .stream(fileItemWriter)\n                // .stream(xmlFileItemWriter)\n                .build();\n    }\n\n    // 输出数据到多个文件\n    private CompositeItemWriter<TestData> multiFileItemWriter() {\n        // 使用CompositeItemWriter代理\n        CompositeItemWriter<TestData> writer = new CompositeItemWriter<>();\n        // 设置具体写代理\n        writer.setDelegates(Arrays.asList(fileItemWriter, xmlFileItemWriter));\n        return writer;\n    }\n\n    // 将数据分类，然后分别输出到对应的文件(此时需要将writer注册到ioc容器，否则报\n    // WriterNotOpenException: Writer must be open before it can be written to)\n    private ClassifierCompositeItemWriter<TestData> classifierMultiFileItemWriter() {\n        ClassifierCompositeItemWriter<TestData> writer = new ClassifierCompositeItemWriter<>();\n        writer.setClassifier((Classifier<TestData, ItemWriter<? super TestData>>) testData -> {\n            try {\n                // id能被2整除则输出到普通文本，否则输出到xml文本\n                return testData.getId() % 2 == 0 ? fileItemWriter : xmlFileItemWriter;\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            return null;\n        });\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/job/XmlFileItemWriterDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.batch.item.xml.StaxEventItemWriter;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.oxm.xstream.XStreamMarshaller;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Component\npublic class XmlFileItemWriterDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n\n    @Bean\n    public Job xmlFileItemWriterJob() throws Exception {\n        return jobBuilderFactory.get(\"xmlFileItemWriterJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() throws Exception {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .writer(xmlFileItemWriter())\n                .build();\n    }\n\n    private StaxEventItemWriter<TestData> xmlFileItemWriter() throws IOException {\n        StaxEventItemWriter<TestData> writer = new StaxEventItemWriter<>();\n\n        // 通过XStreamMarshaller将TestData转换为xml\n        XStreamMarshaller marshaller = new XStreamMarshaller();\n\n        Map<String,Class<TestData>> map = new HashMap<>(1);\n        map.put(\"test\", TestData.class);\n\n        marshaller.setAliases(map); // 设置xml标签\n\n        writer.setRootTagName(\"tests\"); // 设置根标签\n        writer.setMarshaller(marshaller);\n\n        FileSystemResource file = new FileSystemResource(\"/Users/mrbird/Desktop/file.xml\");\n        Path path = Paths.get(file.getPath());\n        if (!Files.exists(path)) {\n            Files.createFile(path);\n        }\n\n        writer.setResource(file); // 设置目标文件路径\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/reader/ItemReaderConfigure.java",
    "content": "package cc.mrbird.batch.reader;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class ItemReaderConfigure {\n\n    @Bean\n    public ListItemReader<TestData> simpleReader() {\n        List<TestData> data = new ArrayList<>();\n        TestData testData1 = new TestData();\n        testData1.setId(1);\n        testData1.setField1(\"11\");\n        testData1.setField2(\"12\");\n        testData1.setField3(\"13\");\n        data.add(testData1);\n        TestData testData2 = new TestData();\n        testData2.setId(2);\n        testData2.setField1(\"21\");\n        testData2.setField2(\"22\");\n        testData2.setField3(\"23\");\n        data.add(testData2);\n        return new ListItemReader<>(data);\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/java/cc/mrbird/batch/writer/ItemWriterConfigure.java",
    "content": "package cc.mrbird.batch.writer;\n\nimport cc.mrbird.batch.entity.TestData;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.batch.item.file.FlatFileItemWriter;\nimport org.springframework.batch.item.file.transform.LineAggregator;\nimport org.springframework.batch.item.xml.StaxEventItemWriter;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.FileSystemResource;\nimport org.springframework.oxm.xstream.XStreamMarshaller;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class ItemWriterConfigure {\n\n    @Bean\n    public FlatFileItemWriter<TestData> fileItemWriter() throws Exception {\n        FlatFileItemWriter<TestData> writer = new FlatFileItemWriter<>();\n\n        FileSystemResource file = new FileSystemResource(\"/Users/mrbird/Desktop/file\");\n        Path path = Paths.get(file.getPath());\n        if (!Files.exists(path)) {\n            Files.createFile(path);\n        }\n\n        writer.setResource(file); // 设置目标文件路径\n\n        // 把读到的每个TestData对象转换为字符串\n        LineAggregator<TestData> aggregator = item -> {\n            try {\n                ObjectMapper mapper = new ObjectMapper();\n                return mapper.writeValueAsString(item);\n            } catch (JsonProcessingException e) {\n                e.printStackTrace();\n            }\n            return \"\";\n        };\n\n        writer.setLineAggregator(aggregator);\n        writer.afterPropertiesSet();\n        return writer;\n    }\n\n    @Bean\n    public StaxEventItemWriter<TestData> xmlFileItemWriter() throws Exception {\n        StaxEventItemWriter<TestData> writer = new StaxEventItemWriter<>();\n\n        // 通过XStreamMarshaller将TestData转换为xml\n        XStreamMarshaller marshaller = new XStreamMarshaller();\n\n        Map<String, Class<TestData>> map = new HashMap<>(1);\n        map.put(\"test\", TestData.class);\n\n        marshaller.setAliases(map); // 设置xml标签\n\n        writer.setRootTagName(\"tests\"); // 设置根标签\n        writer.setMarshaller(marshaller);\n\n        FileSystemResource file = new FileSystemResource(\"/Users/mrbird/Desktop/file.xml\");\n        Path path = Paths.get(file.getPath());\n        if (!Files.exists(path)) {\n            Files.createFile(path);\n        }\n\n        writer.setResource(file); // 设置目标文件路径\n        return writer;\n    }\n}\n"
  },
  {
    "path": "69.spring-batch-itemwriter/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456"
  },
  {
    "path": "70.spring-batch-itemprocessor/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-itemprocessor</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-itemprocessor</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/SpringBatchItemprocessorApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchItemprocessorApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchItemprocessorApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/TestData.java",
    "content": "package cc.mrbird.batch.entity;\n\nimport javax.validation.constraints.NotBlank;\n\n/**\n * @author MrBird\n */\npublic class TestData {\n\n    private int id;\n    private String field1;\n    private String field2;\n    @NotBlank\n    private String field3;\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getField1() {\n        return field1;\n    }\n\n    public void setField1(String field1) {\n        this.field1 = field1;\n    }\n\n    public String getField2() {\n        return field2;\n    }\n\n    public void setField2(String field2) {\n        this.field2 = field2;\n    }\n\n    public String getField3() {\n        return field3;\n    }\n\n    public void setField3(String field3) {\n        this.field3 = field3;\n    }\n\n    @Override\n    public String toString() {\n        return \"TestData{\" +\n                \"id=\" + id +\n                \", field1='\" + field1 + '\\'' +\n                \", field2='\" + field2 + '\\'' +\n                \", field3='\" + field3 + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/job/BeanValidatingItemProcessorDemo.java",
    "content": "package cc.mrbird.batch.entity.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.batch.item.validator.BeanValidatingItemProcessor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class BeanValidatingItemProcessorDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n\n    @Bean\n    public Job beanValidatingItemProcessorJob() throws Exception {\n        return jobBuilderFactory.get(\"beanValidatingItemProcessorJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() throws Exception {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .processor(beanValidatingItemProcessor())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private BeanValidatingItemProcessor<TestData> beanValidatingItemProcessor() throws Exception {\n        BeanValidatingItemProcessor<TestData> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();\n        // 开启过滤，不符合规则的数据被过滤掉；\n        beanValidatingItemProcessor.setFilter(true);\n        beanValidatingItemProcessor.afterPropertiesSet();\n        return beanValidatingItemProcessor;\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/job/CompositeItemProcessorDemo.java",
    "content": "package cc.mrbird.batch.entity.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport cc.mrbird.batch.processor.TestDataFilterItemProcessor;\nimport cc.mrbird.batch.processor.TestDataTransformItemPorcessor;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.batch.item.support.CompositeItemProcessor;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class CompositeItemProcessorDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n    @Autowired\n    private TestDataFilterItemProcessor testDataFilterItemProcessor;\n    @Autowired\n    private TestDataTransformItemPorcessor testDataTransformItemPorcessor;\n\n    @Bean\n    public Job compositeItemProcessorJob() {\n        return jobBuilderFactory.get(\"compositeItemProcessorJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .processor(compositeItemProcessor())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    // CompositeItemProcessor组合多种中间处理器\n    private CompositeItemProcessor<TestData, TestData> compositeItemProcessor() {\n        CompositeItemProcessor<TestData, TestData> processor = new CompositeItemProcessor<>();\n        List<ItemProcessor<TestData, TestData>> processors = Arrays.asList(testDataFilterItemProcessor, testDataTransformItemPorcessor);\n        // 代理两个processor\n        processor.setDelegates(processors);\n        return processor;\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/job/TestDataFilterItemProcessorDemo.java",
    "content": "package cc.mrbird.batch.entity.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport cc.mrbird.batch.processor.TestDataFilterItemProcessor;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class TestDataFilterItemProcessorDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n    @Autowired\n    private TestDataFilterItemProcessor testDataFilterItemProcessor;\n\n    @Bean\n    public Job testDataFilterItemProcessorJob() {\n        return jobBuilderFactory.get(\"testDataFilterItemProcessorJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .processor(testDataFilterItemProcessor)\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/job/TestDataTransformItemPorcessorDemo.java",
    "content": "package cc.mrbird.batch.entity.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport cc.mrbird.batch.processor.TestDataFilterItemProcessor;\nimport cc.mrbird.batch.processor.TestDataTransformItemPorcessor;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class TestDataTransformItemPorcessorDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n    @Autowired\n    private TestDataTransformItemPorcessor testDataTransformItemPorcessor;\n\n    @Bean\n    public Job testDataTransformItemPorcessorJob() {\n        return jobBuilderFactory.get(\"testDataTransformItemPorcessorJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .processor(testDataTransformItemPorcessor)\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/job/ValidatingItemProcessorDemo.java",
    "content": "package cc.mrbird.batch.entity.job;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.batch.item.validator.ValidatingItemProcessor;\nimport org.springframework.batch.item.validator.ValidationException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class ValidatingItemProcessorDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private ListItemReader<TestData> simpleReader;\n\n    @Bean\n    public Job validatingItemProcessorJob() {\n        return jobBuilderFactory.get(\"validatingItemProcessorJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<TestData, TestData>chunk(2)\n                .reader(simpleReader)\n                .processor(validatingItemProcessor())\n                .writer(list -> list.forEach(System.out::println))\n                .build();\n    }\n\n    private ValidatingItemProcessor<TestData> validatingItemProcessor() {\n        ValidatingItemProcessor<TestData> processor = new ValidatingItemProcessor<>();\n        processor.setValidator(value -> {\n            // 对每一条数据进行校验\n            if (\"\".equals(value.getField3())) {\n                // 如果field3的值为空串，则抛异常\n                throw new ValidationException(\"field3的值不合法\");\n            }\n        });\n        return processor;\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/entity/reader/ItemReaderConfigure.java",
    "content": "package cc.mrbird.batch.entity.reader;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class ItemReaderConfigure {\n\n    @Bean\n    public ListItemReader<TestData> simpleReader() {\n        List<TestData> data = new ArrayList<>();\n        TestData testData1 = new TestData();\n        testData1.setId(1);\n        testData1.setField1(\"11\");\n        testData1.setField2(\"12\");\n        testData1.setField3(\"13\");\n        data.add(testData1);\n        TestData testData2 = new TestData();\n        testData2.setId(2);\n        testData2.setField1(\"21\");\n        testData2.setField2(\"22\");\n        testData2.setField3(\"23\");\n        data.add(testData2);\n        TestData testData3 = new TestData();\n        testData3.setId(3);\n        testData3.setField1(\"31\");\n        testData3.setField2(\"32\");\n        testData3.setField3(\"\");\n        data.add(testData3);\n        return new ListItemReader<>(data);\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/processor/TestDataFilterItemProcessor.java",
    "content": "package cc.mrbird.batch.processor;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class TestDataFilterItemProcessor implements ItemProcessor<TestData, TestData> {\n    @Override\n    public TestData process(TestData item) {\n        // 返回null，会过滤掉这条数据\n        return \"\".equals(item.getField3()) ? null : item;\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/java/cc/mrbird/batch/processor/TestDataTransformItemPorcessor.java",
    "content": "package cc.mrbird.batch.processor;\n\nimport cc.mrbird.batch.entity.TestData;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class TestDataTransformItemPorcessor implements ItemProcessor<TestData, TestData> {\n    @Override\n    public TestData process(TestData item) {\n        // field1值拼接 hello\n        item.setField1(item.getField1() + \" hello\");\n        return item;\n    }\n}\n"
  },
  {
    "path": "70.spring-batch-itemprocessor/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456"
  },
  {
    "path": "71.spring-batch-listener/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-listener</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-listener</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/SpringBatchListenerApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchListenerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchListenerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/job/CompositeJobExecutionListenerJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.JobExecution;\nimport org.springframework.batch.core.JobExecutionListener;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.core.listener.CompositeJobExecutionListener;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Arrays;\n\n/**\n * @author MrBird\n */\n@Component\npublic class CompositeJobExecutionListenerJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job compositeJobExecutionListenerJob() {\n        return jobBuilderFactory.get(\"compositeJobExecutionListenerJob\")\n                .start(step())\n                .listener(compositeJobExecutionListener())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .tasklet((contribution, chunkContext) -> {\n                    System.out.println(\"执行步骤....\");\n                    return RepeatStatus.FINISHED;\n                }).build();\n    }\n\n    private CompositeJobExecutionListener compositeJobExecutionListener() {\n        CompositeJobExecutionListener listener = new CompositeJobExecutionListener();\n\n        // 任务监听器1\n        JobExecutionListener jobExecutionListenerOne = new JobExecutionListener() {\n            @Override\n            public void beforeJob(JobExecution jobExecution) {\n                System.out.println(\"任务监听器One，before job execute: \" + jobExecution.getJobInstance().getJobName());\n            }\n\n            @Override\n            public void afterJob(JobExecution jobExecution) {\n                System.out.println(\"任务监听器One，after job execute: \" + jobExecution.getJobInstance().getJobName());\n            }\n        };\n        // 任务监听器2\n        JobExecutionListener jobExecutionListenerTwo = new JobExecutionListener() {\n            @Override\n            public void beforeJob(JobExecution jobExecution) {\n                System.out.println(\"任务监听器Two，before job execute: \" + jobExecution.getJobInstance().getJobName());\n            }\n\n            @Override\n            public void afterJob(JobExecution jobExecution) {\n                System.out.println(\"任务监听器Two，after job execute: \" + jobExecution.getJobInstance().getJobName());\n            }\n        };\n        // 聚合\n        listener.setListeners(Arrays.asList(jobExecutionListenerOne, jobExecutionListenerTwo));\n        return listener;\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/job/ListenerTestJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.listener.*;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class ListenerTestJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private MyJobExecutionListener myJobExecutionListener;\n    @Autowired\n    private MyStepExecutionListener myStepExecutionListener;\n    @Autowired\n    private MyChunkListener myChunkListener;\n    @Autowired\n    private MyItemReaderListener myItemReaderListener;\n    @Autowired\n    private MyItemProcessListener myItemProcessListener;\n    @Autowired\n    private MyItemWriterListener myItemWriterListener;\n\n    @Bean\n    public Job listenerTestJob() {\n        return jobBuilderFactory.get(\"listenerTestJob\")\n                .start(step())\n                .listener(myJobExecutionListener)\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .listener(myStepExecutionListener)\n                .<String, String>chunk(2)\n                .faultTolerant()\n                .listener(myChunkListener)\n                .reader(reader())\n                .listener(myItemReaderListener)\n                .processor(processor())\n                .listener(myItemProcessListener)\n                .writer(list -> list.forEach(System.out::println))\n                .listener(myItemWriterListener)\n                .build();\n    }\n\n    private ItemReader<String> reader() {\n        List<String> data = Arrays.asList(\"java\", \"c++\", \"javascript\", \"python\");\n        return new simpleReader(data);\n    }\n\n    private ItemProcessor<String, String> processor() {\n        return item -> item + \" language\";\n    }\n}\n\nclass simpleReader implements ItemReader<String> {\n    private Iterator<String> iterator;\n\n    public simpleReader(List<String> data) {\n        this.iterator = data.iterator();\n    }\n\n    @Override\n    public String read() {\n        return iterator.hasNext() ? iterator.next() : null;\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyChunkListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.ChunkListener;\nimport org.springframework.batch.core.scope.context.ChunkContext;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyChunkListener implements ChunkListener {\n    @Override\n    public void beforeChunk(ChunkContext context) {\n        System.out.println(\"before chunk: \" + context.getStepContext().getStepName());\n    }\n\n    @Override\n    public void afterChunk(ChunkContext context) {\n        System.out.println(\"after chunk: \" + context.getStepContext().getStepName());\n    }\n\n    @Override\n    public void afterChunkError(ChunkContext context) {\n        System.out.println(\"before chunk error: \" + context.getStepContext().getStepName());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyItemProcessListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.ItemProcessListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyItemProcessListener implements ItemProcessListener<String, String> {\n    @Override\n    public void beforeProcess(String item) {\n        System.out.println(\"before process: \" + item);\n    }\n\n    @Override\n    public void afterProcess(String item, String result) {\n        System.out.println(\"after process: \" + item + \" result: \" + result);\n    }\n\n    @Override\n    public void onProcessError(String item, Exception e) {\n        System.out.println(\"on process error: \" + item + \" , error message: \" + e.getMessage());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyItemReaderListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.ItemReadListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyItemReaderListener implements ItemReadListener<String> {\n    @Override\n    public void beforeRead() {\n        System.out.println(\"before read\");\n    }\n\n    @Override\n    public void afterRead(String item) {\n        System.out.println(\"after read: \" + item);\n    }\n\n    @Override\n    public void onReadError(Exception ex) {\n        System.out.println(\"on read error: \" + ex.getMessage());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyItemWriterListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.ItemWriteListener;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyItemWriterListener implements ItemWriteListener<String> {\n\n    @Override\n    public void beforeWrite(List<? extends String> items) {\n        System.out.println(\"before write: \" + items);\n    }\n\n    @Override\n    public void afterWrite(List<? extends String> items) {\n        System.out.println(\"after write: \" + items);\n    }\n\n    @Override\n    public void onWriteError(Exception exception, List<? extends String> items) {\n        System.out.println(\"on write error: \" + items + \" , error message: \" + exception.getMessage());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyJobExecutionListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.JobExecution;\nimport org.springframework.batch.core.JobExecutionListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyJobExecutionListener implements JobExecutionListener {\n\n    @Override\n    public void beforeJob(JobExecution jobExecution) {\n        System.out.println(\"before job execute: \" + jobExecution.getJobInstance().getJobName());\n    }\n\n    @Override\n    public void afterJob(JobExecution jobExecution) {\n        System.out.println(\"after job execute: \" + jobExecution.getJobInstance().getJobName());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/java/cc/mrbird/batch/listener/MyStepExecutionListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.StepExecution;\nimport org.springframework.batch.core.annotation.AfterStep;\nimport org.springframework.batch.core.annotation.BeforeStep;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyStepExecutionListener {\n\n    @BeforeStep\n    public void breforeStep(StepExecution stepExecution) {\n        System.out.println(\"before step execute: \" + stepExecution.getStepName());\n    }\n\n    @AfterStep\n    public void afterStep(StepExecution stepExecution) {\n        System.out.println(\"after step execute: \" + stepExecution.getStepName());\n    }\n}\n"
  },
  {
    "path": "71.spring-batch-listener/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456"
  },
  {
    "path": "72.spring-batch-exception/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-exception</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-exception</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/SpringBatchExceptionApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchExceptionApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchExceptionApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/exception/MyJobExecutionException.java",
    "content": "package cc.mrbird.batch.exception;\n\n/**\n * @author MrBird\n */\npublic class MyJobExecutionException extends  Exception{\n\n    private static final long serialVersionUID = 7168487913507656106L;\n\n    public MyJobExecutionException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/job/DefaultExceptionJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ExecutionContext;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class DefaultExceptionJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job defaultExceptionJob() {\n        return jobBuilderFactory.get(\"defaultExceptionJob\")\n                .start(\n                    stepBuilderFactory.get(\"step\")\n                        .tasklet((stepContribution, chunkContext) -> {\n                            // 获取执行上下文\n                            ExecutionContext executionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();\n                            if (executionContext.containsKey(\"success\")) {\n                                System.out.println(\"任务执行成功\");\n                                return RepeatStatus.FINISHED;\n                            } else {\n                                String errorMessage = \"处理任务过程发生异常\";\n                                System.out.println(errorMessage);\n                                executionContext.put(\"success\", true);\n                                throw new RuntimeException(errorMessage);\n                            }\n\n                        }).build()\n                ).build();\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/job/RestartJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Isolation;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.interceptor.DefaultTransactionAttribute;\n\nimport java.util.ArrayList;\nimport java.util.stream.IntStream;\n\n/**\n * @author MrBird\n */\n@Component\npublic class RestartJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job restartJob() {\n        return jobBuilderFactory.get(\"restartJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<String, String>chunk(2)\n                .reader(listItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                // .allowStartIfComplete(true)\n                .startLimit(1)\n                .build();\n    }\n\n    private ListItemReader<String> listItemReader() {\n        ArrayList<String> datas = new ArrayList<>();\n        IntStream.range(0, 5).forEach(i -> datas.add(String.valueOf(i)));\n        return new ListItemReader<>(datas);\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/job/RetryExceptionJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.exception.MyJobExecutionException;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.stream.IntStream;\n\n/**\n * @author MrBird\n */\n@Component\npublic class RetryExceptionJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job retryExceptionJob() {\n        return jobBuilderFactory.get(\"retryExceptionJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<String, String>chunk(2)\n                .reader(listItemReader())\n                .processor(myProcessor())\n                .writer(list -> list.forEach(System.out::println))\n                .faultTolerant() // 配置错误容忍\n                .retry(MyJobExecutionException.class) // 配置重试的异常类型\n                .retryLimit(3) // 重试3次，三次过后还是异常的话，则任务会结束，\n                // 异常的次数为reader，processor和writer中的总数，这里仅在processor里演示异常重试\n                .build();\n    }\n\n    private ListItemReader<String> listItemReader() {\n        ArrayList<String> datas = new ArrayList<>();\n        IntStream.range(0, 5).forEach(i -> datas.add(String.valueOf(i)));\n        return new ListItemReader<>(datas);\n    }\n\n    private ItemProcessor<String, String> myProcessor() {\n        return new ItemProcessor<String, String>() {\n            private int count;\n            @Override\n            public String process(String item) throws Exception {\n                System.out.println(\"当前处理的数据：\" + item);\n                if (count >= 2) {\n                    return item;\n                } else {\n                    count++;\n                    throw new MyJobExecutionException(\"任务处理出错\");\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/job/SkipExceptionJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.exception.MyJobExecutionException;\nimport cc.mrbird.batch.listener.MySkipListener;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.stream.IntStream;\n\n/**\n * @author MrBird\n */\n@Component\npublic class SkipExceptionJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n    @Autowired\n    private MySkipListener mySkipListener;\n\n    @Bean\n    public Job skipExceptionJob() {\n        return jobBuilderFactory.get(\"skipExceptionJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        return stepBuilderFactory.get(\"step\")\n                .<String, String>chunk(2)\n                .reader(listItemReader())\n                .processor(myProcessor())\n                .writer(list -> list.forEach(System.out::println))\n                .faultTolerant() // 配置错误容忍\n                .skip(MyJobExecutionException.class) // 配置跳过的异常类型\n                .skipLimit(1) // 最多跳过1次，1次过后还是异常的话，则任务会结束，\n                // 异常的次数为reader，processor和writer中的总数，这里仅在processor里演示异常跳过\n                .listener(mySkipListener)\n                .build();\n    }\n\n    private ListItemReader<String> listItemReader() {\n        ArrayList<String> datas = new ArrayList<>();\n        IntStream.range(0, 5).forEach(i -> datas.add(String.valueOf(i)));\n        return new ListItemReader<>(datas);\n    }\n\n    private ItemProcessor<String, String> myProcessor() {\n        return item -> {\n            System.out.println(\"当前处理的数据：\" + item);\n            if (\"2\".equals(item)) {\n                throw new MyJobExecutionException(\"任务处理出错\");\n            } else {\n                return item;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/job/TransactionJobDemo.java",
    "content": "package cc.mrbird.batch.job;\n\nimport cc.mrbird.batch.exception.MyJobExecutionException;\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.Step;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.item.ItemProcessor;\nimport org.springframework.batch.item.support.ListItemReader;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Isolation;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.interceptor.DefaultTransactionAttribute;\n\nimport java.util.ArrayList;\nimport java.util.stream.IntStream;\n\n/**\n * @author MrBird\n */\n@Component\npublic class TransactionJobDemo {\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job transactionJob() {\n        return jobBuilderFactory.get(\"transactionJob\")\n                .start(step())\n                .build();\n    }\n\n    private Step step() {\n        DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();\n        attribute.setPropagationBehavior(Propagation.REQUIRED.value());\n        attribute.setIsolationLevel(Isolation.DEFAULT.value());\n        attribute.setTimeout(30);\n\n        return stepBuilderFactory.get(\"step\")\n                .<String, String>chunk(2)\n                .reader(listItemReader())\n                .writer(list -> list.forEach(System.out::println))\n                .readerIsTransactionalQueue()\n                .transactionAttribute(attribute)\n                .build();\n    }\n\n    private ListItemReader<String> listItemReader() {\n        ArrayList<String> datas = new ArrayList<>();\n        IntStream.range(0, 5).forEach(i -> datas.add(String.valueOf(i)));\n        return new ListItemReader<>(datas);\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/java/cc/mrbird/batch/listener/MySkipListener.java",
    "content": "package cc.mrbird.batch.listener;\n\nimport org.springframework.batch.core.SkipListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MySkipListener implements SkipListener<String, String> {\n    @Override\n    public void onSkipInRead(Throwable t) {\n        System.out.println(\"在读取数据的时候遇到异常并跳过，异常：\" + t.getMessage());\n    }\n\n    @Override\n    public void onSkipInWrite(String item, Throwable t) {\n        System.out.println(\"在输出数据的时候遇到异常并跳过，待输出数据：\" + item + \"，异常：\" + t.getMessage());\n    }\n\n    @Override\n    public void onSkipInProcess(String item, Throwable t) {\n        System.out.println(\"在处理数据的时候遇到异常并跳过，待输出数据：\" + item + \"，异常：\" + t.getMessage());\n    }\n}\n"
  },
  {
    "path": "72.spring-batch-exception/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/springbatch\n    username: root\n    password: 123456\n"
  },
  {
    "path": "73.spring-batch-launcher/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-batch-launcher</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-batch-launcher</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-batch</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "73.spring-batch-launcher/src/main/java/cc/mrbird/batch/SpringBatchLauncherApplication.java",
    "content": "package cc.mrbird.batch;\n\nimport org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableBatchProcessing\npublic class SpringBatchLauncherApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBatchLauncherApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "73.spring-batch-launcher/src/main/java/cc/mrbird/batch/configure/JobConfigure.java",
    "content": "package cc.mrbird.batch.configure;\n\nimport org.springframework.batch.core.configuration.JobRegistry;\nimport org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class JobConfigure {\n\n    /**\n     * 注册JobRegistryBeanPostProcessor bean\n     * 用于将任务名称和实际的任务关联起来\n     */\n    @Bean\n    public JobRegistryBeanPostProcessor processor(JobRegistry jobRegistry, ApplicationContext applicationContext) {\n        JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor();\n        postProcessor.setJobRegistry(jobRegistry);\n        postProcessor.setBeanFactory(applicationContext.getAutowireCapableBeanFactory());\n        return postProcessor;\n    }\n}\n"
  },
  {
    "path": "73.spring-batch-launcher/src/main/java/cc/mrbird/batch/controller/JobController.java",
    "content": "package cc.mrbird.batch.controller;\n\nimport org.springframework.batch.core.Job;\nimport org.springframework.batch.core.JobParameters;\nimport org.springframework.batch.core.JobParametersBuilder;\nimport org.springframework.batch.core.launch.JobLauncher;\nimport org.springframework.batch.core.launch.JobOperator;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"job\")\npublic class JobController {\n\n    @Autowired\n    private Job job;\n    @Autowired\n    private JobLauncher jobLauncher;\n    @Autowired\n    private JobOperator jobOperator;\n\n    @GetMapping(\"launcher/{message}\")\n    public String launcher(@PathVariable String message) throws Exception {\n        JobParameters parameters = new JobParametersBuilder()\n                .addString(\"message\", message)\n                .toJobParameters();\n        // 将参数传递给任务\n        jobLauncher.run(job, parameters);\n        return \"success\";\n    }\n\n    @GetMapping(\"operator/{message}\")\n    public String operator(@PathVariable String message) throws Exception {\n        // 传递任务名称，参数使用 kv方式\n        jobOperator.start(\"job\", \"message=\" + message);\n        return \"success\";\n    }\n}\n"
  },
  {
    "path": "73.spring-batch-launcher/src/main/java/cc/mrbird/batch/job/MyJob.java",
    "content": "package cc.mrbird.batch.job;\n\nimport org.springframework.batch.core.*;\nimport org.springframework.batch.core.configuration.annotation.JobBuilderFactory;\nimport org.springframework.batch.core.configuration.annotation.StepBuilderFactory;\nimport org.springframework.batch.repeat.RepeatStatus;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Map;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyJob{\n\n    @Autowired\n    private JobBuilderFactory jobBuilderFactory;\n    @Autowired\n    private StepBuilderFactory stepBuilderFactory;\n\n    @Bean\n    public Job job(){\n        return jobBuilderFactory.get(\"job\")\n                .start(step())\n                .build();\n    }\n\n    private Step step(){\n        return stepBuilderFactory.get(\"step\")\n                .tasklet((stepContribution, chunkContext) -> {\n                    StepExecution stepExecution = chunkContext.getStepContext().getStepExecution();\n                    Map<String, JobParameter> parameters = stepExecution.getJobParameters().getParameters();\n                    System.out.println(parameters.get(\"message\").getValue());\n                    return RepeatStatus.FINISHED;\n                })\n                .listener(this)\n                .build();\n    }\n}\n"
  },
  {
    "path": "73.spring-batch-launcher/src/main/resources/application.yml",
    "content": "spring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://localhost:3306/springbatch?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8\n    username: root\n    password: 123456\n  batch:\n    job:\n      enabled: false"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n<modelVersion>4.0.0</modelVersion>\n<parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-cloud-alibaba-nacos-register</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n</parent>\n\n<artifactId>consumer</artifactId>\n<name>consumer</name>\n<description>服务消费端</description>\n\n<build>\n    <plugins>\n        <plugin>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-maven-plugin</artifactId>\n        </plugin>\n    </plugins>\n</build>\n</project>\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/consumer/src/main/java/cc/mrbird/consumer/ConsumerApplication.java",
    "content": "package cc.mrbird.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/consumer/src/main/java/cc/mrbird/consumer/configure/ConsumerConfigure.java",
    "content": "package cc.mrbird.consumer.configure;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author MrBird\n */\n@Configuration\npublic class ConsumerConfigure {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n}\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/consumer/src/main/java/cc/mrbird/consumer/controller/ConsumeController.java",
    "content": "package cc.mrbird.consumer.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"consume\")\npublic class ConsumeController {\n\n    @Autowired\n    private LoadBalancerClient loadBalancerClient;\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"hello/{message}\")\n    public String hello(@PathVariable String message) {\n        ServiceInstance serviceInstance = loadBalancerClient.choose(\"provider\");\n        String path = String.format(\"http://%s:%s/provide/%s\", serviceInstance.getHost(), serviceInstance.getPort(), message);\n        String result = restTemplate.getForObject(path, String.class);\n        return String.format(\"%s from %s %s\", result, serviceInstance.getHost(), serviceInstance.getPort());\n    }\n}\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9001\nspring:\n  application:\n    name: consumer\n  cloud:\n    nacos:\n      server-addr: localhost:8848"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-cloud-alibaba-nacos-register</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>provider</module>\n        <module>consumer</module>\n    </modules>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>\n        <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${com-alibaba-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n</project>"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>cc.mrbird</groupId>\n        <artifactId>spring-cloud-alibaba-nacos-register</artifactId>\n        <version>1.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>provider</artifactId>\n    <name>provider</name>\n    <description>服务提供端</description>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/provider/src/main/java/cc/mrbird/provider/ProviderApplication.java",
    "content": "package cc.mrbird.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n}\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/provider/src/main/java/cc/mrbird/provider/controller/HelloController.java",
    "content": "package cc.mrbird.provider.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\n@RequestMapping(\"provide\")\npublic class HelloController {\n\n    @GetMapping(\"{message}\")\n    public String hello(@PathVariable String message) {\n        return String.format(\"hello %s\", message);\n    }\n}\n"
  },
  {
    "path": "74.spring-cloud-alibaba-nacos-register/provider/src/main/resources/application.yml",
    "content": "server:\n  port: 8001\nspring:\n  application:\n    name: provider\n  cloud:\n    nacos:\n      server-addr: localhost:8848"
  },
  {
    "path": "75.spring-cloud-alibaba-nacos-config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-cloud-alibaba-nacos-config</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-cloud-alibaba-nacos-config</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>\n        <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${com-alibaba-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "75.spring-cloud-alibaba-nacos-config/src/main/java/cc/mrbird/nacos/SpringCloudAlibabaNacosConfigApplication.java",
    "content": "package cc.mrbird.nacos;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringCloudAlibabaNacosConfigApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringCloudAlibabaNacosConfigApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "75.spring-cloud-alibaba-nacos-config/src/main/java/cc/mrbird/nacos/controller/TestController.java",
    "content": "package cc.mrbird.nacos.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\n@RefreshScope\npublic class TestController {\n\n    @Value(\"${message:null}\")\n    private String message;\n    @Value(\"${ext1:null}\")\n    private String ext1;\n    @Value(\"${ext2:null}\")\n    private String ext2;\n\n    @GetMapping(\"message\")\n    public String getMessage() {\n        return this.message;\n    }\n\n    @GetMapping(\"multi\")\n    public String multiConfig() {\n        return String.format(\"ext1: %s ext2: %s\", ext1, ext2);\n    }\n}\n"
  },
  {
    "path": "75.spring-cloud-alibaba-nacos-config/src/main/resources/application.yml",
    "content": "server:\n  port: 8080\nspring:\n  application:\n    name: my-project\n"
  },
  {
    "path": "75.spring-cloud-alibaba-nacos-config/src/main/resources/bootstrap.yml",
    "content": "spring:\n#  profiles:\n#    active: dev\n  cloud:\n    nacos:\n      config:\n        server-addr: localhost:8848\n#        file-extension: yaml\n#        prefix: febs\n#        namespace: '2ef2186e-078c-4904-8643-ff5e90555456'\n#        group: GROUP_A\n#        extension-configs:\n#          - dataId: ext-config-one.yaml\n#            group: DEFAULT_GROUP\n#            refresh: true\n#          - dataId: ext-config-one.yaml\n#            group: DEFAULT_GROUP\n#            refresh: false\n        shared-configs: ext-config-one.yaml,ext-config-two.yaml\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-boot-websocket-socketjs</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-boot-websocket-socketjs</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/src/main/java/cc/mrbird/socket/SpringBootWebsocketSocketjsApplication.java",
    "content": "package cc.mrbird.socket;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringBootWebsocketSocketjsApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringBootWebsocketSocketjsApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/src/main/java/cc/mrbird/socket/configure/WebSocketServerConfigure.java",
    "content": "package cc.mrbird.socket.configure;\n\nimport cc.mrbird.socket.handler.MyStringWebSocketHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.config.annotation.EnableWebSocket;\nimport org.springframework.web.socket.config.annotation.WebSocketConfigurer;\nimport org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;\n\n/**\n * @author MrBird\n */\n@Configuration\n@EnableWebSocket\npublic class WebSocketServerConfigure implements WebSocketConfigurer {\n\n    @Autowired\n    private MyStringWebSocketHandler myStringWebSocketHandler;\n\n    @Override\n    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n        registry.addHandler(myStringWebSocketHandler, \"/connect\").withSockJS();\n    }\n}\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/src/main/java/cc/mrbird/socket/handler/MyStringWebSocketHandler.java",
    "content": "package cc.mrbird.socket.handler;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\n/**\n * @author MrBird\n */\n@Component\npublic class MyStringWebSocketHandler extends TextWebSocketHandler {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Override\n    public void afterConnectionEstablished(WebSocketSession session) {\n        log.info(\"和客户端建立连接\");\n    }\n\n    @Override\n    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {\n        session.close(CloseStatus.SERVER_ERROR);\n        log.error(\"连接异常\", exception);\n    }\n\n    @Override\n    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {\n        super.afterConnectionClosed(session, status);\n        log.info(\"和客户端断开连接\");\n    }\n\n    @Override\n    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {\n        // 获取到客户端发送过来的消息\n        String receiveMessage = message.getPayload();\n        log.info(receiveMessage);\n        // 发送消息给客户端\n        session.sendMessage(new TextMessage(fakeAi(receiveMessage)));\n        // 关闭连接\n        // session.close(CloseStatus.NORMAL);\n    }\n\n    private static String fakeAi(String input) {\n        if (input == null || \"\".equals(input)) {\n            return \"你说什么？没听清︎\";\n        }\n        return input.replace('你', '我')\n                .replace(\"吗\", \"\")\n                .replace('?', '!')\n                .replace('？', '！');\n    }\n}\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/src/main/resources/application.properties",
    "content": "\n"
  },
  {
    "path": "76.spring-boot-websocket-socketjs/src/main/resources/static/client.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>WebSocket客户端</title>\n    <script src=\"https://cdn.bootcss.com/sockjs-client/0.3.4/sockjs.min.js\"></script>\n    <link href=\"https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css\" rel=\"stylesheet\">\n</head>\n<body>\n<style>\n    .jumbotron {\n        width: 100%;\n    }\n\n    #text {\n        height: 3rem;\n        font-size: 1rem;\n        line-height: 3rem;\n        margin: 1rem;\n    }\n\n    .btn {\n        margin-right: 5px;\n    }\n\n    #connect {\n        margin-left: 1rem;\n    }\n\n    #log {\n        margin: 1rem 0 0 1rem;\n    }\n\n</style>\n<div class=\"container\">\n    <div class=\"row\">\n        <div class=\"jumbotron\">\n            <input type=\"text\" placeholder=\"请输入你想传输的内容\" id=\"text\" class=\"col-lg-12\"/>\n            <input type=\"button\" value=\"连接\" class=\"btn btn-info\" id=\"connect\" onclick=\"connect()\"/>\n            <input type=\"button\" value=\"发送\" class=\"btn btn-success\" id=\"sent\" disabled=\"disabled\" onclick=\"sent()\"/>\n            <input type=\"button\" value=\"断开\" class=\"btn btn-danger\" id=\"disconnect\" disabled=\"disabled\"\n                   onclick=\"disconnect()\"/>\n\n            <div id=\"log\">\n                <p>聊天记录:</p>\n            </div>\n        </div>\n    </div>\n</div>\n<script type=\"text/javascript\">\n    let text = document.querySelector('#text');\n    let connectBtn = document.querySelector(\"#connect\");\n    let sentBtn = document.querySelector(\"#sent\");\n    let disconnectBtn = document.querySelector(\"#disconnect\");\n    let logDiv = document.querySelector(\"#log\");\n\n    let ws = null;\n\n    function connect() {\n        let targetUri = \"/connect\";\n        ws = new SockJS(targetUri);\n        ws.onopen = function () {\n            setConnected(true);\n            log('和服务端连接成功！');\n        };\n        ws.onmessage = function (event) {\n            log('服务端说：' + event.data);\n        };\n        ws.onclose = function () {\n            setConnected(false);\n            log('和服务端断开连接！')\n        }\n    }\n\n    function sent() {\n        if (ws != null) {\n            ws.send(text.value);\n            log('客户端说：' + text.value);\n        } else {\n            log('请先建立连接！')\n        }\n    }\n\n    function disconnect() {\n        if (ws != null) {\n            ws.close();\n            ws = null;\n        }\n        setConnected(false);\n    }\n\n    function log(value) {\n        let content = document.createElement('p');\n        content.innerHTML = value;\n        logDiv.appendChild(content);\n        text.value = '';\n    }\n\n    function setConnected(connected) {\n        connectBtn.disabled = connected;\n        disconnectBtn.disabled = !connected;\n        sentBtn.disabled = !connected;\n    }\n</script>\n</body>\n</html>"
  },
  {
    "path": "77.spring-cloud-alibaba-sentinel-dashboard-guide/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.5.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-cloud-alibaba-sentinel-dashboard-guide</artifactId>\n    <version>0.0.1-SNAPSHOT</version>\n    <name>spring-cloud-alibaba-sentinel-dashboard-guide</name>\n    <description>Demo project for Spring Boot</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>\n        <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${com-alibaba-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "77.spring-cloud-alibaba-sentinel-dashboard-guide/src/main/java/cc/mrbird/sentinel/SpringCloudAlibabaSentinelFlowControlApplication.java",
    "content": "package cc.mrbird.sentinel;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringCloudAlibabaSentinelFlowControlApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringCloudAlibabaSentinelFlowControlApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "77.spring-cloud-alibaba-sentinel-dashboard-guide/src/main/java/cc/mrbird/sentinel/controller/TestController.java",
    "content": "package cc.mrbird.sentinel.controller;\n\nimport cc.mrbird.sentinel.service.HelloService;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * @author MrBird\n */\n@RestController\npublic class TestController {\n\n    private Logger log = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private HelloService helloService;\n\n    @GetMapping(\"test1\")\n    public String test1() {\n        throw new RuntimeException(\"服务异常\");\n        // return \"test1\";\n    }\n\n    @GetMapping(\"test2\")\n    public String test2() {\n        return \"test2 \" + helloService.hello();\n    }\n\n    @GetMapping(\"buy\")\n    @SentinelResource(value = \"buy\")\n    public String buy(String goodName, Integer count) {\n        return \"买\" + count + \"份\" + goodName;\n    }\n}"
  },
  {
    "path": "77.spring-cloud-alibaba-sentinel-dashboard-guide/src/main/java/cc/mrbird/sentinel/service/HelloService.java",
    "content": "package cc.mrbird.sentinel.service;\n\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport org.springframework.stereotype.Service;\n\n/**\n * @author MrBird\n */\n@Service\npublic class HelloService {\n\n    @SentinelResource(\"hello\")\n    public String hello() {\n        return \"hello\";\n    }\n}\n"
  },
  {
    "path": "77.spring-cloud-alibaba-sentinel-dashboard-guide/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\nspring:\n  application:\n    name: my-project\n  cloud:\n    sentinel:\n      transport:\n        dashboard: localhost:8080\n        port: 8719\n      web-context-unify: false"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>cc.mrbird</groupId>\n        <artifactId>spring-cloud-alibaba-sentinelresource</artifactId>\n        <version>1.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>consumer</artifactId>\n    <name>consumer</name>\n    <description>服务消费者</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/consumer/src/main/java/cc/mrbird/consumer/ConsumerApplication.java",
    "content": "package cc.mrbird.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n    @Bean\n    @LoadBalanced\n    public RestTemplate restTemplate(){\n        return new RestTemplate();\n    }\n}\n"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/consumer/src/main/resources/application.yml",
    "content": "server:\n  port: 9091\nspring:\n  application:\n    name: consumer\n  cloud:\n    nacos:\n      server-addr: localhost:8001\n    sentinel:\n      transport:\n        dashboard: localhost:8080\n        port: 8719\n"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cc.mrbird</groupId>\n    <artifactId>spring-cloud-alibaba-sentinelresource</artifactId>\n    <version>1.0-SNAPSHOT</version>\n    <packaging>pom</packaging>\n\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <modules>\n        <module>provider</module>\n        <module>consumer</module>\n    </modules>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>\n        <com-alibaba-cloud.version>2.2.0.RELEASE</com-alibaba-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${com-alibaba-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n</project>"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <parent>\n        <groupId>cc.mrbird</groupId>\n        <artifactId>spring-cloud-alibaba-sentinelresource</artifactId>\n        <version>1.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>provider</artifactId>\n    <name>provider</name>\n    <description>服务提供者</description>\n\n    <properties>\n        <java.version>1.8</java.version>\n    </properties>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/provider/src/main/java/cc/mrbird/provider/ProviderApplication.java",
    "content": "package cc.mrbird.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "78.spring-cloud-alibaba-sentinelresource/provider/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\nspring:\n  application:\n    name: provider\n  cloud:\n    nacos:\n      server-addr: localhost:8001\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 MrBird\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "readme.md",
    "content": "## Spring 系列教程\n该仓库为个人博客[https://mrbird.cc](https://mrbird.cc)中Spring系列源码，包含Spring Boot、Spring Boot & Shiro、Spring Cloud，Spring Boot & Spring Security & Spring Security OAuth2，如果该系列教程对您有帮助的话，还请点个star给予精神支持！🐤\n\n### Spring Boot教程 \n1. [开启Spring Boot](https://mrbird.cc/%E5%BC%80%E5%90%AFSpring-Boot.html)\n2. [Spring Boot基础配置](https://mrbird.cc/Spring-Boot%20basic%20config.html)\n3. [Spring Boot中使用MyBatis](https://mrbird.cc/Spring-Boot%20Mybatis.html)\n4. [Spring Boot中使用JdbcTemplate](https://mrbird.cc/Spring-Boot%20JdbcTemplate.html)\n5. [Spring Boot MyBatis配置Druid多数据源](https://mrbird.cc/Spring-Boot-MyBatis%20Druid.html)\n6. [Spring Boot JdbcTemplate配置Druid多数据源](https://mrbird.cc/Spring-Boot-JdbcTemplate%20Druid.html)\n7. [Spring Boot AOP记录用户操作日志](https://mrbird.cc/Spring-Boot-AOP%20log.html)\n8. [Spring Boot中使用thymeleaf](https://mrbird.cc/Spring-Boot%E4%BD%BF%E7%94%A8thymeleaf.html)\n9. [Spring Boot中使用Redis缓存数据](https://mrbird.cc/Spring-Boot%20cache.html)\n10. [Spring Boot中使用Ehcache缓存数据](https://mrbird.cc/Spring-Boot%20cache.html)\n11. [Spring Boot中的JSON技术](https://mrbird.cc/Spring-Boot%20JSON.html)\n12. [Spring Boot中编写单元测试](https://mrbird.cc/Spring-Boot%20TESTing.html)\n13. [Spring Boot整合Swagger2构建RESTful API](https://mrbird.cc/Spring-Boot-Swagger2-RESTful-API.html)\n14. [使用Actuator监控Spring Boot应用](https://mrbird.cc/Acutator-Spring-Boot.html)\n15. [使用Spring Boot发送邮件](https://mrbird.cc/Spring-Boot-Email.html)\n16. [使用Spring Boot Admin监控服务](https://mrbird.cc/Spring-Boot-Admin.html)\n17. [Spring Boot Devtools热部署](https://mrbird.cc/Spring-Boot-Devtools.html)\n18. [Spring Boot logback日志配置](https://mrbird.cc/Spring-Boot-logback.html)\n19. [Spring Boot项目打包成war包](https://mrbird.cc/Spring-Boot%20war.html)\n20. [Linux下部署Spring Boot jar](https://mrbird.cc/Linux%20Spring-Boot-jar.html)\n21. [Spring Boot中使用Jsoup防御XSS攻击](https://mrbird.cc/Jsoup%20XSS.html)\n22. [Spring Boot异常处理](https://mrbird.cc/Spring-Boot-Exception.html)\n23. [Spring Boot中使用过滤器和拦截器](https://mrbird.cc/Spring-Boot-Filter-Interceptor.html)\n24. [Spring Boot整合MyBatis通用Mapper和PageHelper](https://mrbird.cc/MyBatis%20common%20Mapper%20PageHelper.html)\n26. [深入学习Spring Boot自动装配](https://mrbird.cc/deepin-springboot-autoconfig.html)\n27. [深入学习Spring Boot中的SpringApplication](https://mrbird.cc/deepin-springboot-application.html)\n28. [Spring Boot配合Hibernate Validator参数校验](https://mrbird.cc/Spring-Boot-Hibernate-Validator-Params-Check.html)\n29. [自定义Spring Boot 内容协商](https://mrbird.cc/Spring-Boot-Diy-Resolver.html)\n30. [Spring Boot 中处理跨域](https://mrbird.cc/Spring-Boot-Deal-CORS.html)\n31. [Spring Boot 中的异步调用](https://mrbird.cc/Spring-Boot-Async.html)\n32. [Spring Boot 整合Kafka](https://mrbird.cc/Spring-Boot-Kafka.html)\n33. [Spring Boot整合Mongo DB](https://mrbird.cc/Spring-Boot-Mongo-DB-CRUD.html)\n34. [Spring Boot 2.0 WebFlux编程](https://mrbird.cc/Spring-Boot-2-0-WebFlux.html)\n35. [Spring Boot WebFlux增删改查样例](https://mrbird.cc/Spring-Boot-WebFlux-CRUD.html)\n36. [Spring Boot整合WebSocket](https://mrbird.cc/Spring-Boot整合WebSocket.html)\n\n### Spring Boot & Shiro教程\n1. [Spring Boot Shiro用户认证](https://mrbird.cc/Spring-Boot-shiro%20Authentication.html)\n2. [Spring Boot Shiro Remember Me](https://mrbird.cc/Spring-Boot-Shiro%20Remember-Me.html)\n3. [Spring Boot Shiro权限控制](https://mrbird.cc/Spring-Boot-Shiro%20Authorization.html)\n4. [Spring Boot Shiro Redis](https://mrbird.cc/Spring-Boot-Shiro%20cache.html)\n5. [Spring Boot Shiro Ehcache](https://mrbird.cc/Spring-Boot-Shiro%20cache.html)\n6. [Spring Boot Thymeleaf中使用Shiro标签](https://mrbird.cc/Spring-Boot-Themeleaf%20Shiro%20tag.html)\n7. [Spring Boot Shiro在线会话管理](https://mrbird.cc/Spring-Boot-Shiro%20session.html)\n8. [Spring Boot Shiro整合JWT](https://github.com/wuyouzhuguli/SpringAll/tree/master/62.Spring-Boot-Shiro-JWT)\n\n### Spring Boot & Security教程\n1. [Spring Boot中开启Spring Security](https://mrbird.cc/Spring-Boot&Spring-Security.html)\n2. [Spring Security自定义用户认证](https://mrbird.cc/Spring-Security-Authentication.html)\n3. [Spring Security添加图形验证码](https://mrbird.cc/Spring-Security-ValidateCode.html)\n4. [Spring Security添加记住我功能](https://mrbird.cc/Spring-Security-RememberMe.html)\n5. [Spring Security短信验证码登录](https://mrbird.cc/Spring-Security-SmsCode.html)\n6. [Spring Security Session管理](https://mrbird.cc/Spring-Security-Session-Manage.html)\n7. [Spring Security退出登录](https://mrbird.cc/Spring-Security-logout.html)\n8. [Spring Security权限控制](https://mrbird.cc/Spring-Security-Permission.html)\n9. [Spring Security OAuth2入门](https://mrbird.cc/Spring-Security-OAuth2-Guide.html)\n10. [Spring Security OAuth2自定义Token获取方式](https://mrbird.cc/Spring-Security-OAuth2-Customize.html)\n11. [Spring Security OAuth2自定义令牌配置](https://mrbird.cc/Spring-Security-OAuth2-Token-Config.html)\n12. [Spring Security OAuth2单点登录](https://mrbird.cc/Spring-Security-OAuth2-SSO.html)\n\n### Spring Cloud教程\n1. [初识Spring Cloud与微服务](https://mrbird.cc/Spring-Cloud%20and%20MicroService.html)\n2. [Spring Cloud Eureka服务治理](https://mrbird.cc/Spring-Cloud-Eureka.html)\n3. [Spring Cloud Ribbon客户端负载均衡](https://mrbird.cc/Spring-Cloud-Ribbon-LoadBalance.html)\n4. [Spring Cloud Hystrix服务容错](https://mrbird.cc/Spring-Cloud-Hystrix-Circuit-Breaker.html)\n5. [Spring Cloud Hystrix Dashboard仪表盘](https://mrbird.cc/Spring-Cloud-Hystrix-Dashboard.html)\n6. [Spring Cloud Hystrix Dashboard仪表盘 & RabbitMQ](https://mrbird.cc/Spring-Cloud-Hystrix-Dashboard.html)\n7. [Spring Cloud Feign 声明式服务调用](https://mrbird.cc/Spring-Cloud-Feign.html)\n8. [Spring Cloud Zuul服务网关](https://mrbird.cc/Spring-Cloud-Zuul-Router.html)\n9. [Spring Cloud Config统一配置管理](https://mrbird.cc/Spring-Cloud-Config.html)\n10. [使用Spring Cloud Bus刷新配置](https://mrbird.cc/Spring-Cloud-Bus.html)\n11. [使用Spring Cloud Sleuth跟踪微服务](https://mrbird.cc/Spring-Cloud-sleuth.html)\n12. [Spring Cloud Consul服务治理](https://mrbird.cc/Spring-Cloud-Consul.html)\n13. [Spring Cloud Alibaba Nacos注册中心](https://mrbird.cc/Spring-Cloud-Alibaba-Nacos注册中心.html)\n14. [Spring Cloud Alibaba Nacos配置中心](https://mrbird.cc/Spring-Cloud-Alibaba-Nacos配置中心.html)\n15. [Spring Cloud Alibaba Sentinel控制台详解](https://mrbird.cc/Sentinel控制台详解.html)\n16. [Spring Cloud Alibaba Sentinel @SentinelResource](https://mrbird.cc/Spring-Cloud-Alibaba-Sentinel-SentinelResource.html)\n\n### Spring Boot && Dubbo教程\n1. [Spring Boot整合Dubbo&Zookeeper](https://mrbird.cc/Spring-Boot-Dubbo-Zookeeper.html)\n2. [监控Dubbo服务](https://mrbird.cc/dubbo-mointor.html)\n3. [Dubbo的高可用](https://mrbird.cc/Dubbo-High-Availability.html)\n\n### Spring Boot && Spring Batch教程\n\n1. [Spring Batch入门](https://mrbird.cc/Spring-Batch入门.html)\n2. [Spring Batch读取数据](https://mrbird.cc/Spring-Batch读取数据.html)\n3. [Spring Batch输出数据](https://mrbird.cc/Spring-Batch输出数据.html)\n4. [Spring Batch处理数据](https://mrbird.cc/Spring-Batch处理数据.html)\n5. [Spring Batch监听器](https://mrbird.cc/Spring-Batch监听器.html)\n6. [Spring Batch异常处理](https://mrbird.cc/Spring-Batch异常处理.html)\n7. [Spring Batch任务调度](https://mrbird.cc/Spring-Batch作业调度.html)\n\n## Spring\n1. [深入学习Spring组件注册](https://mrbird.cc/Spring-Bean-Regist.html)\n2. [深入学习Spring Bean生命周期](https://mrbird.cc/Spring-Bean-Lifecycle.html)\n3. [深入理解Spring BeanPostProcessor & InstantiationAwareBeanPostProcessor](https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring-BeanPostProcessor-InstantiationAwareBeanPostProcessor.html)\n4. [深入理解BeanFactoryPostProcessor & BeanDefinitionRegistryPostProcessor](https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3BeanFactoryPostProcessor-BeanDefinitionRegistryPostProcessor.html)\n5. [深入理解Spring AOP原理](https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring-AOP%E5%8E%9F%E7%90%86.html)\n6. [Spring声明式事务原理](https://mrbird.cc/Spring%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E5%8E%9F%E7%90%86.html)\n7. [深入理解Spring事件发布与监听](https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E4%BA%8B%E4%BB%B6%E5%8F%91%E5%B8%83%E4%B8%8E%E7%9B%91%E5%90%AC.html)\n8. [深入理解Spring循环依赖](https://mrbird.cc/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Spring%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.html)\n\n持续更新中...\n"
  }
]