SpringBoot 基础项目配置(快速启动)

项目配置


返回实体


无泛型实体


import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: 雨同我
 * @Description:
 * @DateTime: 2022/7/7 20:53
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    @ApiModelProperty(value="是否成功")
    private Boolean success;

    @ApiModelProperty(value="返回码")
    private Integer code;

    @ApiModelProperty(value="返回消息")
    private String message;

    @ApiModelProperty(value="返回数据")
    private Map<String, Object> data = new HashMap<>();



    public static Result ok(){
        Result r = new Result();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    public static Result error(){
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public static Result error(String message){
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage(message);
        return r;
    }

    public Result code(Integer code){
        this.setCode(code);
        return this;                // this的意思就是谁调用就返回谁
    }

    public Result success(Boolean success){
        this.setSuccess(success);
        return this;                // this的意思就是谁调用就返回谁
    }

    public Result message(String message){
        this.setMessage(message);
        return this;
    }
    public Result data(Map<String, Object> data){
        this.data = data;
        return this;
    }
    public Result data(String key,Object value){
        this.data.put(key, value);
        return this;
    }
}


  • 与前端绑定的码,记得看情况更改
public interface ResultCode {
    public static Integer SUCCESS = 20000;
    public static Integer ERROR = 20001;
}



泛型实体


image-20221031210054936

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 通用返回对象
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {
    private long code;
    private String message;
    private T data;


    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }

    /**
     * 失败返回结果
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(ResultCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
    }

    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
    }
    public static <T> CommonResult<T> failed(Integer code,String message) {
        return new CommonResult<T>(code, message, null);
    }
    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }

    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }

    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }

    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized(T data) {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
    }

    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
    }

}

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * 枚举了一些常用API操作码
 * Created by macro on 2019/4/19.
 */
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode{
    SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败"),
    VALIDATE_FAILED(404, "参数检验失败"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "没有相关权限");
    private long code;
    private String message;
}



自定义异常


异常类



import com.lu.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@ControllerAdvice
public class GloablExceptonHandler {
  //指定出现什么异常执行这个方法
   @ExceptionHandler(Exception.class)
   @ResponseBody
   public Result error(Exception e){
      e.printStackTrace();
      return Result.error().message(e.getMessage());
   }


    @ExceptionHandler(GuliException.class)
    @ResponseBody
    public Result error(GuliException e){
        log.info("进入了全局异常处理");
        e.printStackTrace();
        log.error(e.getMsg());
        return Result.error().code(e.getCode()).message(e.getMsg());
    }
}



import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Api(tags = "自定义异常类")
public class DiyException extends RuntimeException{

   @ApiModelProperty(value = "状态码")
   private Integer code; //状态码
   private String msg;//异常信息
}


使用方式


 throw new GuliException(50000,"错误");

  • 返回一个对象


image-20220830210837926



参数校验自定义异常





import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.List;
import java.util.Set;


@RestControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {

    /**
     * 统一处理请求参数校验(普通传参)
     *
     * @param e ConstraintViolationException
     * @return FebsResponse
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder message = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            Path path = violation.getPropertyPath();
            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
            message.append(pathArr[1]).append(violation.getMessage()).append(",");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        return message.toString();
    }

    /**
     * 统一处理请求参数校验(实体对象传参)
     *
     * @param e BindException
     * @return FebsResponse
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String validExceptionHandler(BindException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append(error.getField()).append(error.getDefaultMessage()).append(",");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        return message.toString();

    }
}



项目技术



Lombok


@Singular和@Builder


  • @Builder 是方便构建不同的构造方法重载

  • @Singular 方便操作集合

  • @Singular 注意他的命名,如果是 lists,就会转换为 list ,如果命名不规范就指定名字

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName("tb_role")
@ApiModel(value="Role对象", description="")
public class Role implements Serializable {
   @Singular
   private List<String> lists;


   @Test
    public void run(){
        ArrayList<String> list = new ArrayList<>();
        list.add("ok");
        list.add("11");
        Role haha = Role.builder().list("122").list("ok").build();
        System.out.println(haha);
    }



MaxWell

需求:

MaxWell 监听mysql数据,绑定RabbitMQ消息队列同步es文章数据



全量备份:可以使用mysqldump直接备份整个库或者是备份其中某一个库或者一个库中的某个表。

增量备份:增量备份是针对于数据库的bin-log日志进行备份的,需要开始数据库的bin-log日志。增量备份是在全量的基础上进行操作的。增量备份主要是靠mysql记录的bin-log日志。(可以把二进制日志保存成每天的一个文件)


快速开始看官网:

Quick Start - Maxwell's Daemon (maxwells-daemon.io)



  • 下面是打开二进制日志,查看,打开方式在上面官网

image-20220826185451426



运行 MaxWell


  • RabbitMq 输出方式

docker run --name maxwell --restart=always  -d  zendesk/maxwell bin/maxwell  --user='数据库用户名' --password='数据库密码'  --host='IP地址'  --producer=rabbitmq --rabbitmq_user='MQ用户名' --rabbitmq_pass='MQ密码' --rabbitmq_host='IP地址' --rabbitmq_port='5672' --rabbitmq_exchange='maxwell_exchange'  --rabbitmq_exchange_type='fanout' --rabbitmq_exchange_durable='true' --filter='exclude: *.*, include: blog.tb_article.article_title = *, include: blog.tb_article.article_content = *, include: blog.tb_article.is_delete = *, include: blog.tb_article.status = *' //运行MaxWell

docker run --name maxwell --rm   -it  zendesk/maxwell bin/maxwell  --user='root' --password='123456789'  --host='192.168.80.80'  --producer=rabbitmq --rabbitmq_user='guest' --rabbitmq_pass='guest' --rabbitmq_host='192.168.80.80' --rabbitmq_port='5672' --rabbitmq_exchange='maxwell_exchange'  --rabbitmq_exchange_type='fanout' --rabbitmq_exchange_durable='true' --filter='exclude: *.*, include: blog-github.tb_article.article_title = *, include: blog-github.tb_article.article_content = *, include: blog-github.tb_article.is_delete = *, include: blog-github.tb_article.status = *'  --producer=stdout
//运行MaxWell

  • 控制台输出方式
docker run --name maxwell --rm   -it  zendesk/maxwell bin/maxwell  --user='root' --password='123456789'  --host='192.168.80.80'   --filter='exclude: *.*, include: blog-github.tb_article.article_title = *, include: blog-github.tb_article.article_content = *, include: blog-github.tb_article.is_delete = *, include: blog-github.tb_article.status = *'  --producer=stdout

  • 这个是控制台输出方式,更新日志

image-20220827165802070



SpringSecurity权限拦截



接口方法


WebMvcConfigurer详解


其他的详解可以参考(95条消息) SpringBoot---WebMvcConfigurer详解_zhangpower1993的博客-CSDN博客_webmvcconfigurer

官网 Api 地址ResourceHandlerRegistry (Spring Framework 5.2.23.BUILD-SNAPSHOT API)


addInterceptors:拦截器

  • addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
  • addPathPatterns:用于设置拦截器的过滤路径规则;addPathPatterns("/**")对所有请求都拦截
  • excludePathPatterns:用于设置不需要拦截的过滤规则
  • 拦截器主要用途:进行用户登录状态的拦截,日志的拦截等。

@Override
public void addInterceptors(InterceptorRegistry registry) {
    super.addInterceptors(registry);
    registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**")
    .excludePathPatterns("/emp/toLogin","/emp/login","/js/**","/css/**","/images/**");
}


addResourceHandlers:静态资源

比如,我们想自定义静态资源映射目录的话,只需重写addResourceHandlers方法即可。

  • 例如图片上传到本地
@Configuration
public class MyWebMvcConfigurerAdapter implements WebMvcConfigurer {
    /**
     * 配置静态访问资源
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/my/**").addResourceLocations("classpath:/my/");
    }
}

  • addResoureHandler:指的是对外暴露的访问路径
  • addResourceLocations:指的是内部文件放置的目录



应用

  • 后面有个 / 记得加,还有前面的 file:

image-20220722150017308



addCorsMappings:跨域



@Override
public void addCorsMappings(CorsRegistry registry) {
    super.addCorsMappings(registry);
    registry.addMapping("/**")
            .allowedHeaders("*")
            .allowedMethods("POST","GET")
            .allowedOrigins("*");
}



自定义认证和授权


  • 认证是用户登录,查询他是否注册过
  • 授权是他在这里的访问权限,是 Root ,还是 User

可以参考:

https://mrbird.cc/Spring-Security-Authentication.html


CSRF

更多详解参考:浅谈CSRF - 简书 (jianshu.com)



(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性



常用的注解和方法详解



@EnableGlobalMethodSecurity

  • (jsr250Enabled = true, prePostEnabled = true, securedEnabled = true)

  • 他们都是实现同一个功能

  • 详解可以参考:[(96条消息) @EnableGlobalMethodSecurity注解详解_chihaihai的博客-CSDN博客](https://blog.csdn.net/chihaihai/article/details/104678864#:~:text=prePostEnabled : prePostEnabled %3D true 会解锁 %40PreAuthorize,和 %40PostAuthorize 两个注解。 从名字就可以看出%40PreAuthorize 注解会在方法执行前进行验证,而 %40PostAuthorize 注解会在方法执行后进行验证。)



prePostEnabled
  • 我就讲解这个即可,其他的可以参考上面的

  • prePostEnabled=true,解锁 @PreAuthorize 和 @PostAuthorize 两个注解

  • 从名字就可以看出@PreAuthorize 注解会在方法执行前进行验证,而 @PostAuthorize 注解会在方法执行后进行验证。



里面的注解详解:

包含的表达式参考: Spring Security Reference

public interface UserService {
    List<User> findAllUsers();

    @PostAuthorize ("returnObject.type == authentication.name")
    User findById(int id);
    
	//  @PreAuthorize("hasRole('ADMIN')") 必须拥有 ROLE_ADMIN 角色。
    // 如果当前主体具有指定的权限,则返回 true
    @hasAnyAuthority(["admin",authority2])
    void updateUser(User user);
    
    @PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")
    void deleteUser(int id);
    
    // @PreAuthorize("principal.username.startsWith('Felordcn')") 用户名开头为 Felordcn 的用户才能访问。
    // @PreAuthorize("#id.equals(principal.username)") 入参 id 必须同当前的用户名相同。
    // @PreAuthorize("#id < 10") 限制只能查询 id 小于 10 的用户
}



@PreAuthorize("hasAuthority('pms:brand:read')")使用

  • 鉴权
  • 一直看底层,看到了下面,是不是有点眼熟

image-20220905170858006


image-20220905171329486


  • 重写里面的方法

image-20220905172707307


  • 数据库字段的权限

image-20220905172939326



整合 JWT 的问题


JwtAuthenticationTokenFilter 为什么不注入容器

代码位置:https://gitee.com/ha6666/java-learn/tree/master/1-springsecurity-jwt

image-20220904111601494



实践问题:

image-20220904111617578

推荐使用:

image-20220904111732590

posted @ 2022-10-31 21:07  雨同我  阅读(370)  评论(0编辑  收藏  举报