spring security 的安全之路环境准备与基础知识复习(一)

第一章:导读以及学习目标

  • 认证与授权
登录的认知:
  • 同时支持多种认证方式
  • 同时支持多种前端渠道
  • 支持集群环境、跨应用工作,Session控制、控制用户权限,防护与身份认证相关的攻击。

学习目标:

  • 可重用的、企业级的、认证和授权模块
  • 深入理解Spring Security及相关框架的原理、功能和代码。
  • 可以基于Spring Security及相关框架独立开发认证授权相关功能。
  • 掌握抽象和封装的常见技巧,可以编写可重用的模块供他人使用。
image.png

前置知识

  • JavaWeb基础
  • Maven基础
  • Spring基础

第二章、开发环境的准备

1.代码结构

  • seapp-security:主模块
  • seapp-security-core: 核心业务逻辑
  • seapp-security-browser:浏览器安全特定代码
  • seapp-security-app:app相关特定代码
  • seapp-security-demo:样例程序

parent工程pom.xml maven依赖的引入:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.seapp.security</groupId>
    <artifactId>seapp-security</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>security-core</module>
        <module>security-browser</module>
        <module>security-app</module>
        <module>security-demo</module>
    </modules>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.M3</spring-cloud.version>
        <seapp-security-version>1.0-SNAPSHOT</seapp-security-version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

<!--    <build>-->
<!--        <plugins>-->
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
<!--        </plugins>-->
<!--    </build>-->

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>


</project>

第三章、使用Spring MVC开发RESTful API

学习目标:

使用Spring MVC编写Restful API
使用Spring MVC处理其它web应用常见的需求和场景
Restful API开发常用辅助框架


1.RESTful API简介

  • 用URL描述资源
  • 使用HTTP方法描述行为。使用HTTP状态码来表示不同的结果。
  • 使用json交互数据。
  • Restful只是一种风格,并不是强制的标准。

2.开发基础的增删改查接口

  • 编写针对RestfulApi的测试用例
  • 使用注解声明RestfulAPI
  • 在RestfulAPI中传递参数
2.1 常用注解
  • @RestController 标明此Controller提供了RestApi
  • @RequestMapping及其变体。映射http请求url到java方法。
  • @RequestParam 映射请求参数到java方法的参数。
    required:true,该参数必传。false,该参数可选。
    defaultValue:当该参数可选时,若没有传值,则使用默认值。
    value: 指定参数名称(当传参与形参不同时使用),等同于name属性。
  • @PageableDefault 指定分页参数默认值。
    page:页数
    size: 每页条目数
    sort:设定排序字段以及排序方式
  • @pathVariable 映射url片段到java方法的参数
  • RequestBody 映射请求体到java方法的参数
  • 日期类型参数的处理
    以时间戳为准,具体如何展示前台界面控制。

2.2 模拟前端测试入门(MockMvc )

package com.seapp.web;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

/**
 * 测试用例
 * @author seapp
 * @date 2020/8/4 17:33
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void before(){
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void whenQuerySuccess() throws Exception {
        //模拟测试get请求
        mockMvc.perform(MockMvcRequestBuilders.get("/user")
                //请求类型设定
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                //请求状态码设定
                .andExpect(MockMvcResultMatchers.status().isOk())
                //请求返回集合长度设定(json返回数据)
               //jsonpath的使用规则:https://github.com/json-path/JsonPath
                .andExpect(MockMvcResultMatchers.jsonPath("$.length()")
                        .value(3));
    }

}


3. 在url声明中使用正则表达式

示例如下:限制传递的id只能为数字。

    @RequestMapping(value = "/user/{id:\\d+}", method = RequestMethod.GET)
    public User getUserInfo(@PathVariable("id") String id) {
        System.out.println("id = " + id);
        User user = new User();
        user.setUsername("tom");
        return user;
    }

4. @JsonView控制json输出内容

目的:在不同需求下对获取到的同一个对象,输出不同的内容。

@JsonView的使用步骤:
  • 使用接口来声明多个视图
  • 在值对象的get方法上指定视图
package com.seapp.dto;

import com.fasterxml.jackson.annotation.JsonView;

/**
 * @author seapp
 * @date 2020/8/4 17:49
 */
public class User {

    //声明简单视图
    public interface UserSimpleView{};
    //声明详细视图,并集成简单视图
    public interface UserDetailView extends UserSimpleView{};

    private String username;
    private String password;
    //在get方法上,指定username在简单视图上展示
    @JsonView(UserSimpleView.class)
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    //在get方法上,指定password在详情视图上展示
    @JsonView(UserDetailView.class)
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  • 在Controller方法上指定视图
package com.seapp.web.controller;

import com.fasterxml.jackson.annotation.JsonView;
import com.seapp.dto.User;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;

import java.awt.print.Pageable;
import java.lang.invoke.MethodType;
import java.net.Authenticator;
import java.util.ArrayList;
import java.util.List;

/**
 * @author seapp
 * @date 2020/8/4 17:32
 */
@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * get请求,路径为"/user"
     *
     * @return
     */
//    @RequestMapping(value = "/user", method = RequestMethod.GET)
    @GetMapping
    @JsonView(User.UserSimpleView.class)//指定返回json数据为简单视图
    public List<User> query(@RequestParam(required = false, defaultValue = "tom", name = "username") String username) {
        System.out.println("username = " + username);
        List<User> userList = new ArrayList<>();
        userList.add(new User());
        userList.add(new User());
        userList.add(new User());
        return userList;
    }


//    @RequestMapping(value = "/user/{id:\\d+}", method = RequestMethod.GET)
    @GetMapping("/{id:\\d+}")
    @JsonView(User.UserDetailView.class)//指定返回json数据为详细视图
    public User getUserInfo(@PathVariable("id") String id) {
        System.out.println("id = " + id);
        User user = new User();
        user.setUsername("tom");
        return user;
    }
}

  • 测试类的实现,以及调用
package com.seapp.web;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MockMvcBuilder;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.lang.annotation.Target;

/**
 * 测试用例
 * @author seapp
 * @date 2020/8/4 17:33
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void before(){
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void whenQuerySuccess() throws Exception {
        //模拟测试get请求
        String string = mockMvc.perform(MockMvcRequestBuilders.get("/user")
                .param("username", "seapp")
                //请求类型设定
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                //请求状态码设定
                .andExpect(MockMvcResultMatchers.status().isOk())
                //请求返回集合长度设定(json返回数据)
                //jsonpath的使用规则:https://github.com/json-path/JsonPath
                .andExpect(MockMvcResultMatchers.jsonPath("$.length()")
                        .value(3))
                .andReturn().getResponse().getContentAsString();
        System.out.println("string = " + string);
    }

    /**
     * 测试查询指定用户id的接口
     * @throws Exception
     */
    @Test
    public void whenGetInfoSuccess() throws Exception {
        String string = mockMvc.perform(MockMvcRequestBuilders.get("/user/1")
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.username")
                        .value("tom"))
                .andReturn().getResponse().getContentAsString();
        System.out.println("string = " + string);
    }

    /**
     * 正则表达式限制,当查询id不是字符串时,提示参数异常。
     * @throws Exception
     */
    @Test
    public void whenGetInfoFail() throws Exception {
        String string = mockMvc.perform(MockMvcRequestBuilders.get("/user/a")
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().is4xxClientError())
                .andReturn().getResponse().getContentAsString();
        System.out.println("string = " + string);
    }

}

5. 校验

  • @Valid注解和BindingResult验证请求参数的合法性并处理校验结果
//注解@NotBlank 元素值不为空
 @NotBlank
 private String password;

//使用@Valid注解实行校验,并通过BindingResult对象来获取校验结果
 @PostMapping
    public User create(@RequestBody @Valid User user, BindingResult errors) {

        if (errors.hasErrors()) {
            errors.getAllErrors().stream()
                    .forEach(error -> System.out.println(error.getDefaultMessage()));
        }

        System.out.println(user.getId());
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getBirthday());
        user.setId(1);
        return user;
    }
//测试调用方法
  @Test
    public void whenCreateSuccess() throws Exception {

        //post请求的json字符串参数
        String content = "{\"username\":\"tom\",\"password\":\"\"}";
        String string = mockMvc.perform(MockMvcRequestBuilders.post("/user")
                .contentType(MediaType.APPLICATION_JSON_UTF8).content(content))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1))
                .andReturn().getResponse().getContentAsString();
        System.out.println("string = " + string);

    }


  • 常用的验证注解

  • 自定义校验注解
    ①注解类的实现:

package com.seapp.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**自定义校验注解
 * @author seapp
 * @date 2020/8/5 9:56
 */
@Target({ElementType.METHOD,ElementType.FIELD})//可标注在方法上或字段上
@Retention(RetentionPolicy.RUNTIME)//在运行时进行校验
@Constraint(validatedBy = MyConstraintValidator.class)//指定校验逻辑所在的类
public @interface MyConstraint {

    String message();

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

②校验规则类的实现:

package com.seapp.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 自定义校验注解的校验逻辑
 * @author seapp
 * @date 2020/8/5 10:00
 *
 * 两个泛型:
 *  第一个:指定自己定义的注解
 *  第二个:指定校验注解可以注解在什么类型的参数上。
 */
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {

    /**
     *注意在该类中,可通过@Autowired注解可以注入任意bean类。
     */


    @Override
    public void initialize(MyConstraint constraintAnnotation) {
        //valid进行初始化
        System.out.println("myvalid is init");
    }

    /**
     *
     * @param value
     * @param constraintValidatorContext
     * @return   true:校验通过,false:校验失败
     */
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
        //实行校验
        System.out.println(value);
        return false;
    }
}

③校验结果的获取

 @PutMapping("/{id:\\d+}")
    public User update(@RequestBody @Valid User user, BindingResult errors) {

        if (errors.hasErrors()) {
            errors.getAllErrors().stream()
                    .forEach(error -> {
                        //获取校验字段名称
                        FieldError fieldError = (FieldError) error;
                        //拼接校验结果展示字符串
                        String message = fieldError.getField() + ":" + error.getDefaultMessage();
                        System.out.println("message = " + message);

                        System.out.println(error.getDefaultMessage());
                    });
        }

        System.out.println(user.getId());
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getBirthday());
        user.setId(1);
        return user;
    }

第四章 RESTful API错误处理

学习目标:

  • Spring Boot中默认的错误处理机制
  • 自定义异常处理

1. Spring Boot对异常的处理机制

//根据发出请求头中是否包含html/text来确定返回数据类型为ModelAndView,还是ResponseEntity转换的json数据。

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController
...
@Override
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections
                .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity<Map<String, Object>>(status);
        }
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        return new ResponseEntity<>(body, status);
    }
...

2. 自定义异常,以及异常处理器的配置

  • 自定义异常:
package com.seapp.exception;

/**自定义运行时异常
 * @author seapp
 * @date 2020/8/5 14:34
 */
public class UserNotExistException extends RuntimeException {

    private String id;

    public UserNotExistException(String message, String id) {
        super(message);
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

  • 异常处理器的配置
package com.seapp.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.HashMap;
import java.util.Map;

/**
 * @author seapp
 * @date 2020/8/5 14:35
 */
@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(UserNotExistException.class)//绑定指定异常类
    @ResponseBody//将返回数据类型转换为json数据格式
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//指定返回状态码
    public Map<String,Object> handlerUserNotExistException(UserNotExistException ex){
        Map<String,Object> result = new HashMap<>();
        result.put("id",ex.getId());
        result.put("message",ex.getMessage());
        return result;
    }

}

3. RESTful API的拦截

  • 过滤器(Filter)
    ①自定义过滤器
package com.seapp.web.filter;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.Date;

/**
 * @author seapp
 * @date 2020/8/5 14:56
 */
@Component
public class TimeFilter implements Filter {

    /**
     * 过滤器初始化方法
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("tiem filter init");
    }

    /**
     * 过滤器执行方法
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("time filter start");
        long start = new Date().getTime();
        filterChain.doFilter(servletRequest,servletResponse);//执行下一个拦截器
        System.out.println("time filter:" + (new Date().getTime() - start));
        System.out.println("time filter finish");
    }

    /**
     * 过滤器销毁
     */
    @Override
    public void destroy() {
        System.out.println("time filter destory");
    }
}

②过滤器配置生效
方式一:
直接在拦截器类上添加@Component注解,注入Spring 容器即可。
方式二:
通过@Configuration Java代码注入的方式实现,这种方式可控制过滤指定的url访问路径。

 package com.seapp.config;

import com.seapp.web.filter.TimeFilter;
import com.seapp.web.interceptor.TimeInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * @author seapp
 * @date 2020/8/5 15:40
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private TimeInterceptor timeInterceptor;

    /**
     * 配置拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }

    /**
     * 通过Java配置过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();

        //将指定的filter加入到bean中。
        TimeFilter timeFilter = new TimeFilter();
        registrationBean.setFilter(timeFilter);

        //指定该过滤器对那些url访问路径生效
        List<String> urls = new ArrayList<>();
        urls.add("/*");//过滤所有的url路径
        registrationBean.setUrlPatterns(urls);

        return registrationBean;
    }

}

  • 拦截器(Interceptor)
    ①:自定义拦截器
package com.seapp.web.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.invoke.MethodHandle;
import java.util.Date;

/**自定义拦截器
 * @author seapp
 * @date 2020/8/5 15:49
 */
@Component
public class TimeInterceptor implements HandlerInterceptor {

    /**
     * 方法调用之前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response
            , Object handler) throws Exception {
        System.out.println("timeInterceptor: preHandle" );

        //设定拦截器开始时间
        request.setAttribute("startTime",new Date().getTime());
        //获取执行方法的类名和方法名
        String className = ((HandlerMethod) handler).getBean().getClass().getName();
        System.out.println("className = " + className);
        String methodName = ((HandlerMethod) handler).getMethod().getName();
        System.out.println("methodName = " + methodName);

        return true;
    }

    /**
     * 控制器方法正常执行之后,执行该方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response
            , Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("timeInterceptor: postHandle" );
        long startTime = (long)request.getAttribute("startTime");
        System.out.println("postHandle 耗时:" +  (new Date().getTime() - startTime));
    }

    /**
     * 无论控制器方法是否执行,该方法都会被执行。
     * 若执行的方法发生异常,则Exception对象中有值。
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response
            , Object handler, Exception ex) throws Exception {
        System.out.println("timeInterceptor: afterCompletion" );
        System.out.println("exception:" + ex);
        long startTime = (long)request.getAttribute("startTime");
        System.out.println("postHandle 耗时:" +  (new Date().getTime() - startTime));
    }
}

②:拦截器的配置

package com.seapp.config;

import com.seapp.web.filter.TimeFilter;
import com.seapp.web.interceptor.TimeInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * @author seapp
 * @date 2020/8/5 15:40
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private TimeInterceptor timeInterceptor;

    /**
     * 配置拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }

    /**
     * 通过Java配置过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();

        //将指定的filter加入到bean中。
        TimeFilter timeFilter = new TimeFilter();
        registrationBean.setFilter(timeFilter);

        //指定该过滤器对那些url访问路径生效
        List<String> urls = new ArrayList<>();
        urls.add("/*");//过滤所有的url路径
        registrationBean.setUrlPatterns(urls);

        return registrationBean;
    }
}
  • 切片(Aspect )


①定义切片,设定切入点。增强方法中获取请求参数

package com.seapp.web.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/** 使用AOP方式,实现在方法执行之前对请求参数的获取。
 * @author seapp
 * @date 2020/8/5 16:39
 */
@Aspect
@Component
public class TimeAspect {

    /**
     * @Before  方法执行之前执行
     * @After   方法执行之后执行
     * @Around  方法前后都执行
     */

    @Around("execution(* com.seapp.web.controller.UserController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {

        System.out.println("aspect is start");

        //在方法执行之前,获取方法参数
        Object[] args = pjp.getArgs();
        for (Object arg :
                args) {
            System.out.println("arg = " + arg);
        }

        //执行控制器方法,返回值为被调用方法的执行结果
        Object obj = pjp.proceed();

        System.out.println("aspect is finish");

        return obj;
    }
}

4. Filter(过滤器)、Interceptor(拦截器)、Aspect(切片)三者的执行顺序

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351