SpringBoot详解

  • Post author:
  • Post category:其他

1.回顾Spring

1.优点
开源,轻量级,非侵入式的一站式框架,简化企业级应用开发。
控制反转(IOC),依赖注入(DI)降低了组件之间的耦合性,实现了软件各层之间的解耦。
面向切面(AOP),利用它可以很容易实现一些拦截,如事务控制等。
spring对于主流的应用框架提供了很好的支持,如mybatis。
spring提供有自己的mvc实现。
2.缺点
虽然Spring的组件代码是轻量级的,但它的配置确实重量级的。虽然Spring引入了注解功能,但是仍然需要编写大量的模板化配置文件。
项目的依赖管理也是一件耗时耗力的事情,在环境搭建时,需要分析要导入大量库的坐标,而且还需要分析导入与之有依赖关,一旦选错依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

2.SpringBoot概述

SpringBoot是由Pivotal团队提供的在Spring框架基础之上开发的框架,其设计目的是用来简化应用的初始搭建以及开发过程。
SpringBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来代替Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。
SpringBoot以约定大于配置的核心思想,从而使开发人员不再需要定义样板化的配置。使他集成了大量常用的第三方库配置(如Redis,MongoDB,Jpa RabbitMQ,Quartz等等),SprringBoot应用中这些第三方库几乎可以零配置的开箱即用,通过这种方式,SpringBoot致力于在蓬勃发展的快速应用开发领域成为领导者。
SpringBoot你只需要“run”就可以非常轻易的构建独立的、生产级别的Spring应用。

3.SpringBoot特点

创建独立的Spring应用程序;
直接内嵌tomcat、jetty和undertow
提供了固定化的“starter”配置,以简化构建配置;
尽可能的自动配置Spring和第三方库;
提供产品级的功能,如:安全指标,运行状况监测和外部化配置等;
绝对不会生成代码,并且不需要XML配置。
1.SpringBoot的核心功能
(1)起步依赖
起步依赖就是将具备某种功能的坐标打包在一起,并提供一些默认的功能。
(2)自动配置
SpringBoot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。

4.SpringBoot开发环境构建

1.方法1:官网配置生成
https://start.spring.io/
选择配置项,在线生成并下载
解压后导入idea中
在这里插入图片描述
2.方法2:idea自动生成
Idea集成了https://start.spring.io/,可以在idea中选择配置并生成
在这里插入图片描述
在这里插入图片描述
选择组件
在这里插入图片描述
项目结构
在这里插入图片描述
SpringBoot集成web组件后,内置了tomcat服务器,一个SpringBoot应用相对于一个独立的服务,可以使用启动类中的main方法启动SpringBoot程序。
在这里插入图片描述
在线生成启动logo:https://www.bootschool.net/ascii
下载存放在resources目录下即可。

5.SpringBoot配置文件

SpringBoot使用一个全局的配置文件(配置文件名是固定的)
application.properties属性文件格式,内容为键值对
server.port=8081
application.yml
yml是YAML(YAML Ain’ t Markup Language)语言的文件,以数据为中心
yml配置示例

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/ssm?characterEncoding=utf-8&useSS=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

#mybatis
mybatis-plus:
  type-aliases-package: com.ff.springBootDemo.bean
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    cache-enabled: true
    map-underscore-to-camel-case: true



logging:
  level:
    com.ff.springBootDemo.dao: trace
  file:
      name: H:\log\log.log

  pagehelper:
    helperDialect: mysql
    reasonable: true
    supportMethodsArguments: true
    params: count=countSql

yaml基本语法
语法结构:key:空格value
以此来表示一对键值对(空格不能省略);以空格的缩进来控制层级关系,只要是左边对齐的一列数据都是同一层级的。
值的写法
字面量:普通的值[数字,布尔值,字符串]
K:V
字面量直接写在后面就可以,字符串默认不用加上双引号或者单引号;
行内写法:friends:{lastName: zhangsan,age: 18}
数组(List,Set)(用-值表示数组中的一个元素)语法示例如下:
animal:
-cat
-dog
-pig
行内写法:
animal: [cat,dog,pig]

6.SpringBoot注解

@SpringBootApplication
这个注解是SpringBoot最核心的注解,用在SpringBoot的主类上,标识这是一个SpringBoot应用,用来开启SpringBoot的各项能力。实际上这个注解是@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合。由于这些注解一般都是一起使用,所以SpringBoot提供了一个统一的注解@SpringBootApplication。
@EnableAutoConfiguration
允许SpringBoot自动配置注解,开启这个注解之后,SpringBoot就能根据当前类路径下的包或者类来配置SpringBean。
如:当前路径下有Mybatis这个JAR包,MybatisAutoConfiguration注解就能根据相关参数来配置Mybatis的各个SpringBean,
@Configuration
用于定义配置类,指出该类是Bean配置的信息源,相当于传统的xml配置文件,一般加在主类上。如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类。
@ComponentScan
组件扫描。让SpringBoot扫描到Configuration类并把它加入到程序上下文。@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到Spring容器中。
@RestController
用于标注控制层组件(如struts中的action),表示这是个控制器bean,并且是将函数的返回值直接填入HTTP响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的合集。
@Bean
相当于XML中的< bean> < /bean>,放在方法的上面,而不是类,意思是产生一个bean,并交给Spring管理。
@PathVariable
路径变量,参数与大括号里的名字一样要相同。

@DeleteMapping(value = "/delete/{id}")
    public Map<String,Object> delete(@PathVariable("id") String id){
        Map<String,Object> map = new HashMap<>();
        try {
            userService.deleteUser(id);
            map.put("code",200);
        }catch (Exception e){
            e.printStackTrace();
            map.put("code",500);
            map.put("msg","删除失败");
        }
        return map;
    }

@ConfigurationProperties
将配置文件中的Person属性赋的值映射到该组件中

@ConfigurationProperties(prefix = "user" )


<dependency> 
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId> 
	<optional>true</optional> 
</dependency>

7.SpringBoot集成JDBC

1.数据源配置
在我们访问数据库的时候,需要配置一下数据源,下面介绍一下几种不同的数据库配置方式。
首先,为了连接数据库需要引入jdbc支持,在pom.xml中引入配置:

<dependency> 
	<groupId>org.springframework.boot</groupId> 
	<artifactId>spring-boot-starter-jdbc</artifactId> 
</dependency>

在application.yml中配置数据源信息

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/ssm?characterEncoding=utf-8&useSS=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

使用JdbcTemplate操作数据库
Spring的JdbcTemplate是自动配置的,可以直接使用@Autowired来注入到自己的bean中来使用。

8.SpringBoot整合阿里数据源

导入阿里数据源jar

<dependency> 
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId> 
	<version>1.1.10</version> 
</dependency>

1.在yml文件中注册阿里数据库连接池:

type: com.alibaba.druid.pool.DruidDataSource 
initialSize: 5 初始化时建立物理连接的个数 
minIdle: 1 最小连接池数量 
maxActive: 20 最大连接池数量

2.添加工具类
Sql监控
http://localhost:9999/druid/login.html

9.SpringBoot集成mybatis

<dependency> 
	<groupId>org.mybatis.spring.boot</groupId> 
	<artifactId>mybatis-spring-boot-starter</artifactId> 
	<version>2.1.4</version> 
</dependency>

在application.yml中配置数据源信息:

#mybatis
mybatis-plus:
  type-aliases-package: com.ff.springBootDemo.bean
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    cache-enabled: true
    map-underscore-to-camel-case: true
   
#日志打印
logging:
  level:
    com.ff.springBootDemo.dao: trace
  file:
      name: H:\log\log.log

10.JWT

Json web token(JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。 因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
1.起源
(1)传统的session认证
http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来。
(2)基于session认证所显露的问题
Session:每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性:用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求再这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF(跨站请求伪造):因为是基于cookie来进行用户识别的,cookie如果被截获,用户很容易受到跨站请求伪造的攻击。
(3)基于token的鉴别机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程上是这样的:
在这里插入图片描述

  1. 用户使用账号和密码发出post请求;
  2. 服务器使用私钥创建一个jwt;
  3. 服务器返回这个jwt给浏览器;
  4. 浏览器将该jwt串在请求头中向服务器发送请求;
  5. 服务器验证该jwt;
  6. 返回响应的资源给浏览器。

2.JWT的主要应用场景
身份认证这种场景下,一旦用户完成了登录,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO) 中比较广泛的使用了该技术。信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的
(1)优点

  1. 简洁(Compact):可以通过URL,POST参数或者在HTTP haeder发送,因为数据量小,传输速度也很快;
  2. 自包含(Self-contained):负载中包含了所有的用户所需要的信息,避免了多次查询数据库;
  3. 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持;
  4. 不需要在服务器端保存会话信息,特别适用于分布式微服务。

3.JWT的构成
JWT是由三段信息构成的,将这三段信息文本用.连接一起就构成了JWT字符串。如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiw ibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab3 0RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload,用户的信息),第三部分是签证(signature)。
(1)第一部分
header
jwt的头部承载两部分信息:
声明类型,这里是jwt
声明加密算法 通常直接使用HMAC HS256
完整的头部就像下面的JSON:
{
‘typ’: ‘JWT’,
‘alg’: ‘HS256’
}
然后将头部进行base64转码,构成了第一部分:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
10010101 01010101 100101 010101 010100
(2)第二部分
payload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
标准中注册的声明;
公共的声明(公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务,需要的必要信息,但不建议加敏感信息,如密码,因为该部分在客户端可解密);
私有的声明。
定义一个payload:
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}

然后将其进行base64转码,得到jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4i OnRydWV9
(3)第三部分
signature
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header(base64后的)
payload(base64后的)
secret
这个部分需要base64转码后的header和base64转码后的payload使用,连接组成的字符串,然后通过header中声明的加密方式进行加密secret组合加密,然后就构成了jwt的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
4.JWT搭建
引入JWT依赖,由于是基于Java,所以需要的是java-jwt

<!--jwt-->
<dependency>
	<groupId>com.auth0</groupId>
	<artifactId>java-jwt</artifactId>
	<version>3.8.2</version>
</dependency>

创建生成token方法

/**
 * JWT工具类
 */
public class JWTUtil {

    /**
     * 根据用户id,账号生成token
     * @param u
     * @return
     */
    public static String getToken(User u) {
        String token = "";
        try {
            //过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间
            Date expireDate = new Date(new Date().getTime() +30*60*1000);
            //秘钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            //设置头部信息
            Map<String,Object> header = new HashMap<>();
            header.put("typ","JWT");
            header.put("alg","HS256");
            //携带id,账号信息,生成签名
            token = JWT.create()
                    .withHeader(header)
                    .withClaim("id",u.getId())
                    .withClaim("account",u.getAccount())
                    .withExpiresAt(expireDate)
                    .sign(algorithm);
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
        return token;
    }
}

验证token是否有效

 /**
     * 验证token是否有效
     * @param token
     * @return
     */
    public static boolean verify(String token){
        try {
            //验签
            Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {//当传过来的token如果有问题,抛出异常
            return false;
        }
    }

获得token中payload部分数据

 /**
     * 获得token 中playload部分数据,按需使用
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token){
        return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
    }

用户登录成功后将用户id和账号存储到token中返回给客户端,之后客户端每次请求将token发送到服务器端验证,在服务器中进行验证。

11.跨域问题

1.什么是跨域?
跨域是指从一个域名的网页去请求另一个域名的资源。比如从 www.baidu.com 页面去请求 www.google.com 的资源。但是一般情况下不会这么做,它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。跨域的严格一点的定义是:只要 协议、域名、端口有任何一个不同,就被当作是跨域,所谓同源是指域名,协议,端口均相同,只要有一个不同,就是跨域。
在这里插入图片描述

在这里插入图片描述
2.为什么浏览器要限制跨域访问呢?
原因就是安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。
3.为什么要跨域?
既然有安全问题,那为什么又要跨域呢?有时公司内部有很多个不同的子域,比 如一个是 location.company.com ,而应用是放在 app.company.com , 这时想 从 app.company.com 去访问 location.company.com 的资源就属于跨域。
4.解决跨域问题的方法
后端
跨域资源共享(CORS)
W3C的Web工作组推荐了一种新的机制,即跨域资源共享(Cross-origin Resource Sharing),简称CORS。其实这个机制就是实现了跨站访问控制,使得安全地进行跨站数据传输成为可能。
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
只需要在后台中加上响应头来允许域请求,在被请求的Response header中加入以下设置,就可以实现跨域访问了。
Spring项目中的解决方法:
(1)手工设置请求头(局部跨越HttpServletResponse):使用HttpServletResponse对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里Origin的值也可以设置为“*”,表示全部放行。

@RequestMapping("/hello") 
@ResponseBody 
public String index(HttpServletResponse response){
 	response.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848"); 
 	return "Hello World";
  }

(2)使用注解(局部跨域@CrossOrigin):在方法上(@RequestMapping)使用注解@CrossOrigin

@RequestMapping("/hello") 
@ResponseBody 
@CrossOrigin("http://127.0.0.1:8848") 
public String index( ){ 
	return "Hello World"; 
}

或者在控制器(@Controller)上使用注解@CrossOrigin。
(3)返回新的CorsFilter(全局跨域):在任意配置类,返回一个新的CorsFilter Bean,并添加映射路径和具体的CORS配置信息。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Collections;
@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        //1,允许任何来源
        corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
        //2,允许任何请求头
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
        //3,允许任何方法
        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
        //4,允许凭证
        corsConfiguration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
}

版权声明:本文为m0_67402026原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。