SpringBoot拦截器的编写

1 说明

      本文将讲述SpringBoot拦截器的编写,通过举一个Token第三方认证的例子让读者了解在SpringBoot中拦截器该如何编写,在讲解拦截器之前,可能有读者会对过滤器和拦截器的区别产生疑问,首先我先介绍一下过滤器和拦截器有哪些区别,具体写代码的时候根据具体场景选择使用。

1.1 过滤器和拦截器的区别

      Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查,日志记录等。不同的是:

  1. 使用范围不同:Filter是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。
  2. 规范不同:Filter是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是Spring容器内的,是Spring框架支持的。
  3. 使用的资源不同:同其他的代码块一样,拦截器也是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IoC注入到拦截器即可;而Filter则不能。
  4. 深度不同:Filter只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。

1.2 过滤器和拦截器所在的位置

在这里插入图片描述

1.3 请求穿透过程

在这里插入图片描述      第二步在SpringMVC中是DispatcherServlet来分发请求给不同的Controller,执行的是Servlet的service()方法。

2 例子:Token第三方认证

      这里构建一个场景,当一个请求携带appToken来访问程序,程序首先校验appToken是否有效,如果无效就拦截请求,这个验证appToken的过程通过判断方法上是否有@ValidToken注解来决定是否校验appToken
代码清单:

  1. WebMvcConfig.java
  2. AppTokenInterceptor.java
  3. SysLoginController.java

1 WebMvcConfig.java

import com.css.common.utils.SpringContextUtils;
import com.css.modules.file.interceptor.AppTokenInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
@EnableWebMvc
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 添加认证拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        AppTokenInterceptor appTokenInterceptor = SpringContextUtils.getBean(AppTokenInterceptor.class);
        registry.addInterceptor(appTokenInterceptor).addPathPatterns("/**");
    }

    /**
     * 将自定义Interceptor添加进IOC容器,不添加没法再其属性类使用依赖注入,注入其他对象实例
     */
    @Bean
    public AppTokenInterceptor appTokenInterceptor() {
        return new AppTokenInterceptor();
    }
}

2 AppTokenInterceptor.java

/**
 * appToken校验拦截器
 *
 * @author 一朝风月
 * @date 2021/6/21 16:40
 */
@Slf4j
public class AppTokenInterceptor implements HandlerInterceptor {
    @Value("${baseUrl}")
    private String baseUrl;

    @Resource
    private RestTemplate restTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (method.getAnnotation(ValidToken.class) == null) {
            return true;
        }
        //从header中获取token
        String appToken = request.getHeader("appToken");
        //如果header中不存在token,则从参数中获取token
        if (StringUtils.isBlank(appToken)) {
            appToken = request.getParameter("appToken");
        }
        if (StringUtils.isEmpty(appToken)) {
            setResponseMsg(response, Result.ok("请输入appToken!"));
            return false;
        }

        String url = this.baseUrl + "/sys/sysApplication/validToken";

        HttpHeaders headers = new HttpHeaders();
        //创建请求头
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> requestEntity = new HttpEntity<>(appToken, headers);

        ResponseEntity<String> responseEntity = this.restTemplate.exchange(
                url, HttpMethod.POST, requestEntity, String.class, new LinkedMultiValueMap<>());
        JSONObject result = JSONObject.parseObject(responseEntity.getBody());
        if (result == null) {
            setResponseMsg(response, Result.error("令牌校验失败,返回null"));
            return false;
        }
        if (result.get(Result.DATA) == null) {
            log.debug(result.get(Result.MSG).toString());
            setResponseMsg(response, Result.error(result.get(Result.MSG).toString()));
            return false;
        }
        if ((boolean) result.get(Result.DATA)) {
            return true;
        } else {
            setResponseMsg(response, Result.ok("令牌校验未通过"));
            return false;
        }
    }

    private void setResponseMsg(HttpServletResponse response, Result result) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().append(JSONObject.toJSONString(result));
    }
}

3 SysLoginController.java(部分代码)

    /**
     * 应用登录
     */
    @ValidToken
    @PostMapping("/sys/appLogin")
    public Map<String, Object> appLogin(@RequestBody SysLoginForm form) {
        //用户信息
        SysUserEntity user = sysUserService.queryByUserName(form.getUsername());
        //账号不存在、密码错误
        if (user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {
            return Result.error("账号或密码不正确");
        }
        //账号锁定
        if (user.getStatus() == 0) {
            return Result.error("账号已被锁定,请联系管理员");
        }
        //生成token,并保存到数据库
        return sysUserTokenService.createToken(user.getUserId());
    }

3 结果

      当令牌过期时

{
    "msg": "appToken已过期,请重新获取",
    "code": 500
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值