• SPRING 扩展组件


      

    1、spring data

    2、spring security

    3、自动配置

    bean装配、属性注入

    配置HTTPS 

    自定义keystore - 使用RSA

    $ keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA

    在配置文件中加载https

    #  配置SSL
    #  ssl:
    #    key-store: classpath:mykeys.jkd
    #    key-store-password: lipengfei
    #    key-password: lipengfei

    4、配置(注入)属性

    package com.jony.boot5.boottest.util;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    
    @Data
    @Component
    @ConfigurationProperties(prefix = "taco.orders")
    /**
     * 使用bean 配置为一个配置类
     */
    
    @Validated // 编译验证
    public class OrderProps {
    
        @Max(value = 30)
        @Min(value = 5)
        private int pageSize;
    }
    taco:
      orders:
        pageSize: 10

    配置属性元数据

    1)依赖

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

    2)文件(idea自动提示可创建)

    {
      "properties": [
        {
          "name": "taco.orders.pageSize",
          "type": "java.lang.String",
          "description": "Description for taco.orders.pageSize."
        },
        {
          "name": "taco.orders.rowNum",
          "type": "java.lang.String",
          "description": "Description for taco.orders.rowNum."
        }
      ]
    }

    3)profile (配置模式切换)

    spring.profiles.active={profile-name}

    创建模式文件-规则 application-{profile-name}.properties 或 application-{profile-name}.yml

    在YAML 中可以通过‘---’三横线创建profiles 模式

    taco:
      orders:
        rowNum: 20
        pageSize: 20
    
    # yml 支持 使用 application-{profie名}.xml 和在同一个文件中设置数据(使用三个分割线 ------
    spring:
      profiles: prod
    server:
      port: 8080
    taco:
      orders:
        pageSize: 10

    在环境变量中设置profiles

    linux :export SPRING_PROFILES_ACTIVE=prod,dev

    windows:

     在windows环境下通过jar启动会加载环境变量,在idea开发环境中不生效

    4)bean 的profile 创建使用@profile注解

    5、rest 服务

    通过rest服务实现前后端分离

    使用postman测试接口服务

    put 与patch

    put - 针对全部替换

    patch - 针对部分更新

     hibernate - id-uuid 序列

        @Id
        @GenericGenerator(name = "idGenerator", strategy = "uuid")
        @GeneratedValue(generator = "idGenerator")
        private String id;

    @PrePersist 、@PreUpdate

    hibernate缓存ResultSet

    @PrePersist 在数据持久化(create)的情况下调用

    @PreUpdate 在数据库中数据发生变化下调用

    package com.jony.boot5.boottest.controller;
    
    import com.jony.boot5.boottest.entity.Country;
    import com.jony.boot5.boottest.repository.CountryRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Optional;
    
    @RestController
    //处理rest请求,accept 内容为json格式
    @RequestMapping(value = "/rest", produces = {"application/json"})
    //允许跨域请求
    @CrossOrigin(origins = {"*"})
    public class RestfulController {
        @Autowired
        private CountryRepository repository;
    
        @GetMapping
        public Iterable findAll() {
            return repository.findAll();
        }
    
        @GetMapping(path = "/{id}")
        public ResponseEntity<Country> findById(@PathVariable("id") String id) {
            Optional<Country> optional = repository.findById(id);
    //        返回状态码
            if (optional.isPresent()) {
    //            返回ok
                return new ResponseEntity<Country>(optional.get(), HttpStatus.OK);
            }
    //返回404
            return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
        }
    
        /**
         * 处理post请求
         *
         * @return
         */
        @PostMapping(consumes = {"application/json"})
    //    consumes 指定用户输入的json数据
        @ResponseStatus(code = HttpStatus.CREATED)
    //    返回created 状态码
        public Country postCountry(@RequestBody Country country) {
            Country result = repository.save(country);
            return result;
        }
    
        @PutMapping(path = "/{id}",consumes = {"application/json"})
        /**
         * 全部更新或插入
         */
        public Country putCountry(@RequestBody Country country,@PathVariable("id") String id){
            country.setId(id);
            return repository.save(country);
        }
    
        @PatchMapping(path = "/{id}")
        /**
         * 更新
         */
        public Country patchCountry(@PathVariable("id")String id,@RequestBody Country patch){
            Country country = repository.findById(id).get();
            if(country!=null){
    //            IF 判断设置
            }
            return repository.save(country);
        }
    }

    针对restful - csrf 伪造

    解决方法:

    1、实现RequestMacher 接口

    2、每次请求中设置csrf 值

    3、使用oauth2 

    package com.jony.boot5.boottest.util;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.security.web.util.matcher.RequestMatcher;
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    
    @Component
    /**
     * 注册bean 重写matcher 组件
     */
    @ConfigurationProperties(prefix = "custom.csrf")
    public class CsrfRequestMacher implements RequestMatcher {
        private final HashSet<String> allowedMethods = new HashSet<>(
                Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
    
        private Set<String> excludeUrls;
    
        public void setExcludeUrls(Set<String> excludeUrls) {
            this.excludeUrls = excludeUrls;
        }
    
        @Override
        public boolean matches(HttpServletRequest request) {
    //        如果是设置在允许范围内的post或put 等请求则直接放过
            for (String exclude:excludeUrls) {
                if(request.getServletPath().startsWith(exclude)){
                    return false;
                }
            }
    //        if (excludeUrls.contains(request.getServletPath())) {
    //            return false;
    //        }
            return !this.allowedMethods.contains(request.getMethod());
        }
    }

    6、超媒体链接(数据显示自身信息与链接)

    添加依赖

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

     关于HATEOAS 稳定新版本更改部分接口的名称

    新建资源类

    package com.jony.boot5.boottest.entity;
    
    import lombok.Data;
    import org.hibernate.annotations.GenericGenerator;
    import org.springframework.hateoas.RepresentationModel;
    
    import javax.persistence.*;
    import java.sql.Timestamp;
    
    //JPA entity
    @Data
    //@NoArgsConstructor(access=AccessLevel.PRIVATE)
    @Entity
    //@Table(name = "country")
    public class CountryResource extends RepresentationModel {
        private String countryname;
    
        private String countrycode;
    
        private byte[] img;
    
        private Timestamp now;
    
        @Column(name = "update_date")
        private Timestamp update;
    
        @PrePersist
        void createDate() {
            now = new Timestamp(System.currentTimeMillis());
        }
    
        @PreUpdate
        void updateDate(){
            update = new Timestamp(System.currentTimeMillis());
        }
    }

     

     

     

    package com.jony.boot5.boottest.entity.assembler;
    
    import com.jony.boot5.boottest.controller.RestfulController;
    import com.jony.boot5.boottest.entity.Country;
    import com.jony.boot5.boottest.entity.CountryResource;
    import org.springframework.hateoas.server.RepresentationModelAssembler;
    import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport;
    
    /**
     * 资源装配器
     */
    public class CountryResourceAssembler extends RepresentationModelAssemblerSupport<Country, CountryResource> {
    
        /**
         * Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
         *
         * @param controllerClass must not be {@literal null}.
         * @param resourceType    must not be {@literal null}.
         */
        public CountryResourceAssembler(Class<?> controllerClass, Class<CountryResource> resourceType) {
            super(controllerClass, resourceType);
        }
    
        public CountryResourceAssembler() {
            super(RestfulController.class,CountryResource.class);
        }
    
        @Override
        protected CountryResource instantiateModel(Country entity) {
            return new CountryResource(entity);
        }
    
        @Override
        public CountryResource toModel(Country entity) {
            return createModelWithId(entity.getId(),entity);
        }
    }
        @GetMapping("/resource")
    //    public CollectionModel<EntityModel<Country>> getAllOnLinks() {
        public CollectionModel<CountryResource> getAllOnLinks() {
    //        获取资源列表
            List<Country> countries = repository.findAll();
    //        包装返回数据
    //        CollectionModel<EntityModel<Country>> resources = CollectionModel.wrap(countries);
    
    //        使用装配器为每一条数据添加链接
    //        在包装的过程中转换资源
            CollectionModel<CountryResource> resources = new CountryResourceAssembler().toCollectionModel(countries);
    
    //        添加linkes
    //        resources.add(
    //                WebMvcLinkBuilder.linkTo(RestfulController.class)
    //                            .slash("resource")
    //                .withRel("resources")
    //        );
    
            resources.add(
                    linkTo(methodOn(getClass()).getAllOnLinks())
                            .withRel("resources")
            );
            return resources;
        }

    配置rest 自动端点

    加载依赖

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

    只需实体与repository 接口即可生成对应接口

    package com.jony.boot5.boottest.repository;
    
    import com.jony.boot5.boottest.entity.RestData;
    import org.springframework.data.repository.PagingAndSortingRepository;
    import org.springframework.web.bind.annotation.CrossOrigin;
    
    @CrossOrigin(origins="*")
    public interface RestDataRepository extends PagingAndSortingRepository<RestData,String> {
    
    }

    配置rest api 基础端点

    spring:
          data:
            rest:
                base-path:/api

    获取可访问的rest资源描述

    http://localhost:8080/api
    {
      "_links": {
        "restdata": {
          "href": "http://localhost:8080/api/rs{?page,size,sort}",
          "templated": true
        },
        "countries": {
          "href": "http://localhost:8080/api/countries"
        },
        "sysUsers": {
          "href": "http://localhost:8080/api/sysUsers"
        },
        "profile": {
          "href": "http://localhost:8080/api/profile"
        }
      }
    }

    针对rest 资源设置别名

    package com.jony.boot5.boottest.entity;
    
    import lombok.Data;
    import org.hibernate.annotations.GenericGenerator;
    import org.springframework.data.rest.core.annotation.RestResource;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.PrePersist;
    import java.sql.Timestamp;
    
    @Data
    @Entity
    @RestResource(path = "rs",rel = "restdata")
    public class RestData {
    
        @Id
        @GenericGenerator(strategy = "uuid",name = "ugen")
        @GeneratedValue(generator = "ugen")
        private String id;
        private String name;
        private Timestamp createDate;
    
        @PrePersist
        void init(){
            createDate = new Timestamp(System.currentTimeMillis());
        }
    }

    分页与排序(repository 集成 pagingAndSortingRepository)

    URL 查询分页(sort 针对一个属性,页数从0开始)

    localhost:8080/api/rs?page=0&size=2&sort=name,desc

    配置自定义端点(特殊业务逻辑)

    package com.jony.boot5.boottest.controller;
    
    import com.jony.boot5.boottest.entity.RestData;
    import com.jony.boot5.boottest.repository.RestDataRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.rest.webmvc.RepositoryRestController;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    import java.util.Optional;
    
    @RepositoryRestController
    public class RestfulDataController {
        @Autowired
        private RestDataRepository repository;
    
        @GetMapping(path = "/rs/sign/{id}")
        public ResponseEntity<RestData> takeRestData(@PathVariable("id") String id){
            Optional<RestData> data = repository.findById(id);
            return new ResponseEntity<RestData>(data.get(),HttpStatus.OK);
        }
    }

    在path中第一个分隔path 必须与api 中已经存在的资源相符(匹配已有的rest服务端点),否则不会成功注册

    返回的数据要么使用ResponseEntity 封装,要么加上@RespouseBody注解

  • 相关阅读:
    URAL 2080 莫队
    Codeforces Round #361 (Div. 2) C D
    UVALive 7297 bfs
    UVALive 7472
    HDU 5773 最长上升子序列
    递归求解最大值和最小值
    数字方阵旋转问题
    实现循环队列的各种基本运算
    实现顺序栈的各种基本运算
    使用两个栈来实现队列
  • 原文地址:https://www.cnblogs.com/jony-it/p/12641873.html
Copyright © 2020-2023  润新知