Spring Boot后端架构设计(一)

Spring Boot后端架构设计(一)

1、自定义异常(表明是业务是出现了异常)

Exception:必须手动显式处理,要么上抛要么捕获

RuntimeException:既可以显式处理,又可以被虚拟机隐式处理(省去了手动处理异常)

@Data
public class CustomException extends RuntimeException {
    private String msg;
    private int code = 500;

    public CustomException(String msg) {
        super(msg);
        this.msg = msg;
    }

    public CustomException(String msg, Throwable e) {
        super(msg, e);
        this.msg = msg;
    }

    public CustomException(String msg, int code) {
        super(msg);
        this.msg = msg;
        this.code = code;
    }

    public CustomException(String msg, int code, Throwable e) {
        super(msg, e);
        this.msg = msg;
        this.code = code;
    }

}

2、封装R对象

数据结构:HashMap

设计模式:静态工厂方法

  1. 状态码
  2. 消息
  3. 数据

导入httpcomponents(里面有很多HTTP状态码)

继承HashMap:因为HashMap可以绑定数据,希望同样可以往R中绑定数据

提供了链式调用

提供预定义好的静态工厂方法来调用,不需要自己new对象

public class R extends HashMap<String, Object> {

    public R() {
        put("code", HttpStatus.SC_OK);
        put("msg", "success");
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }

    public static R ok() {
        return new R();
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R error(String msg) {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
    }

    public static R error() {
        return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
    }

}

3、整合Swagger

4、添加后端验证

@ApiModel 类

@ApiModelProperty 属性

@ApiModel
@Data
public class TestSayHelloForm {
    @NotBlank
    @Pattern(regexp = "^[\\u4e00-\\u9fa5]{2,15}$",message = "不符合正则表达式")
    @ApiModelProperty("姓名")
    private String name;
}

validation 校验

1、引入依赖

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

2、创建Form类(web方法的参数中需要加上@Valid)

@PostMapping("/sayHello")
@ApiOperation("测试方法")
public R sayHello(@Valid @RequestBody TestSayHelloForm form) {
    return R.ok().put("message", "Hello,"+form.getName());
}

5、抵御XSS攻击

设计模式:装饰器模式

HttpServletRequest是一个接口,如果想要重新定义请求类,这接口中的抽象方法太多了,不应该扩展这个接口。

简单自定义请求类:

继承XssHttpServletRequestWrapper父类

JavaEE是一个标准,具体的实现由服务器厂商完成

举例:

Tomcat在实现Servlet规范时,定义了HttpServletRequest接口的实现类。

同时,JavaEE规范还定义了XssHttpServletRequestWrapper请求类的包装类

用到了装饰器模式,无论服务器厂商怎么实现HttpServletRequest接口,只需要继承HttpServletRequestWrapper然后覆盖某个方法即可,然后把请求传入请求包装类(XssFilter–>XssHttpServletRequestWrapper)。

用户的代码和服务器厂商的代码完全解耦,我们不需要关心HttpServletRequest接口是怎么实现的,借助包装类我们可以随意修改请求中的方法,装饰器模式是一种优雅的代码设计。

如果要增加请求的功能,除了子类做方法覆盖之外我可以使用HttpServletRequestWrapper类,这个类是用了装饰器模式,就像给请求套上了手机壳,只需要覆盖Wrapper类的方法,就能做到覆盖厂商请求对象里方法

XssHttpServletRequestWrapper

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

创建过滤器,把Request对象传入Wrapper对象

XssFilter

//普通的Servlet过滤器是优先执行的,是早于Spring MVC框架执行的 
@WebFilter(urlPatterns = "/*")//拦截请求的路径(所有路径) 
public class XssFilter implements Filter {

总结:

对请求里的数据做转义,做转义的时候把转义的代码写到HttpServletRequestWrapper类的方法里面,然后在项目里面创建一个过滤器,所有的请求都传入Wrapper对象,这样我们的转义就可以生效了。

1、XssFilter

2、XssHttpServletRequestWrapper

3、启动类加

@ServletComponentScan //如果没有这个filter**就不会生效

6、Shiro和JWT

Shiro

  1. 认证(是否登录)
  2. 授权(是否有权限)

为什么选用Shiro

Spring Security必须用在Spring项目中,Shiro的普及度更高

原理:

利用HttpSession或Redis来存储Token

利用Filter过滤器对每个Http请求过滤

缺点:使用HttpSession来保存用户登录凭证,保存在服务端,只适用于单体项目

集群模式下(多态Tomcat),进行负载均衡时,会出现问题。

解决:

引入JWT实现单点登录

JWT

将登录凭证加密后,以令牌字符串的方式保存在客户端,不需要关心后端部署架构,客户端需要在请求头中加上token,后端去验证。

并且JWT可以兼容:客户端,浏览器、小程序、物联网设备

这样就实现了全局的单点登录功能,把登录凭证加密保存在客户端的机制就叫做JWT

使用到了多态:

//扩展AuthenticationToken接口
public class OAuth2Token implements AuthenticationToken {

AuthenticationToken是接口类型,定义令牌的封装类的时候实现了这个接口,所以在这是一个多态的写法,传入的是这个接口的子类对象

@Override
public boolean supports(AuthenticationToken token) {
    return token instanceof OAuth2Token;//OAuth2Token是自己写的
}

令牌刷新机制

HttpSession机制:http超时时间为15分钟,HttpSession对象会自动续期

JWT没有这种机制

解决:在Java后端增加令牌自动续期的机制

方案一:双令牌机制

生成过期时间一短一长的令牌字符串,都保存在客户端,如果短的过期了,长短没有过期,重新生成两个令牌。

方案二:缓存令牌机制

令牌不但要保存在客户端,还要保存在Redis

缓存令牌的过期时间是客户端令牌的一倍,如果客户端令牌过期了,缓存令牌没有过期,则生成新的令牌。

OAuth2Filter

Filter拦截请求、响应

把新生成的令牌写到响应里面返回给客户端

1、令牌有效–>放行

2、过期:缓存令牌没有过期–>续期(JwtUtil),缓存令牌过期–>重新登录

新生成的令牌字符串要存储在两个位置

ThreadLocalToken封装类、Redis

多线程使用同一个对象会有并发读写问题

ThreadLocalToken封装类:给每个线程分配一个独立的保险柜(对象)

ThreadLocal是线程安全的

涉及到AOP、多线程

3、定义AOP切面

Filter类需要放行请求给web方法,返回R对象

定义一个AOP切面拦截器,拦截web方法返回的R对象,拦截到R对象后,从ThreadLocal中取出Token绑定在R对象里面。

总结:

ThreadLocalToken类是在OAuth2Filter(生产者)和TokenAspect(消费者)切面类之间传递数据的(第三方媒介)

同一个线程取出的数据和就是之前存的数据,和其他线程没有关系

7、精简返回客户端的异常

Spring MVC框架返回给客户端的异常消息包括执行栈等详细信息

@ControllerAdvice可以全局捕获SpringMVC异常

根据异常类型返回不同的提示

/**
 * 精简返回给客户端的异常
 */
//引用日志模块
@Slf4j
//ControllerAdvice这个注解可以捕获Spring抛出的各种异常,因为后端项目Restful风格的调用,所以加上Rest
@RestControllerAdvice
public class ExceptionAdvice {

    @ResponseBody //方法返回的字符串需要写到响应里面
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //响应的状态码 500
    @ExceptionHandler(Exception.class) //捕获Spring MVC抛出的各种异常(全局捕获异常),Exception子类的异常
    public String exceptionHandler(Exception e){//方法的名字随便定义

        log.error("执行异常",e);

        //根据异常的类型加以判断,返回不同的异常消息

        //后端验证失败抛出的异常
        if(e instanceof MethodArgumentNotValidException){
            //强制转换
            MethodArgumentNotValidException exception= (MethodArgumentNotValidException) e;
            //获得具体的异常消息(精简的原因),将错误消息返回给前台
            //直接getMsg获得的内容太多了,还包含执行栈的内容
            return exception.getBindingResult().getFieldError().getDefaultMessage();
        }
        else if(e instanceof EmosException){
            EmosException exception= (EmosException) e;
            return exception.getMsg();//直接取出异常对象里面的消息就行了
        }
        else if(e instanceof UnauthorizedException){
            return "你不具备相关权限";
        }
        else{
            return "后端执行异常";
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值