美文网首页
spring security 的安全之路环境准备与基础知识复习

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

作者: Seapp | 来源:发表于2020-08-05 17:15 被阅读0次

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

    • 认证与授权
    登录的认知:
    • 同时支持多种认证方式
    • 同时支持多种前端渠道
    • 支持集群环境、跨应用工作,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(切片)三者的执行顺序

    相关文章

      网友评论

          本文标题:spring security 的安全之路环境准备与基础知识复习

          本文链接:https://www.haomeiwen.com/subject/rexlrktx.html