spring security入门

1. Restful API和传统API的区别

  •  用URL描述资源
  •  用http描述方法行为,用http状态码描述结果
  •  使用json交互数据
  • RESTful是一种风格,不是强制的标准

 2. 使用spring mvc开发Restful API

2.1 请求参数校验

   1)实体类

import com.fasterxml.jackson.annotation.JsonView;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.util.Date;

public class User {
    public interface UserSimpleView{}
    public interface UserDetailView extends UserSimpleView{}

    @JsonView(UserSimpleView.class)
    private Integer id;

    @JsonView(UserSimpleView.class)
    @NotBlank
    private String username;

    @JsonView(UserDetailView.class)
    private String password;

    @JsonView(UserSimpleView.class)
    @Range(max = 200, min = 0)
    @NotNull
    private Integer age;

    @JsonView(UserSimpleView.class)
    private Date birthday;
}
User.java

  2)控制器,如果不加BindingResult参数,并且出异常则不进入方法,直接将错误信息返回

    @PostMapping
    public User create(@Valid @RequestBody User user, BindingResult errors) {
        if (errors.hasErrors()) {
            errors.getAllErrors().forEach(error -> {
                System.out.println("error ==> "+error.getDefaultMessage());
            });
        }

        System.out.println(user);
        user.setBirthday(new Date());
        return user;
    }

2.2 选择性的返回实体类的属性,JsonView

1)实体类

public class User {
    public interface UserSimpleView{}
    public interface UserDetailView extends UserSimpleView{}

    @JsonView(UserSimpleView.class)
    private Integer id;

    @JsonView(UserSimpleView.class)
    @NotBlank
    private String username;

    @JsonView(UserDetailView.class)
    private String password;

    @JsonView(UserSimpleView.class)
    @Range(max = 200, min = 0)
    @NotNull
    private Integer age;

    @JsonView(UserSimpleView.class)
    private Date birthday;
}
User.java

2)控制器

    @GetMapping
    @JsonView(User.UserSimpleView.class)
    public List<User> query(UserQueryCondition condition,
                            @PageableDefault(page = 1, size = 10, sort = "age") Pageable pageable) {
        System.out.println(condition);
        List<User> users = new ArrayList<>();
        users.add(new User() {{
            setId(1);
            setUsername("admin1");
            setPassword("123");
            setAge(20);
        }});
        users.add(new User() {{
            setId(2);
            setUsername("admin2");
            setPassword("123");
            setAge(22);
        }});
        return users;
    }
UserController.java

2.3 请求URL参数变量

  使用正则表达式对请求URL参数做限制

@GetMapping("/{id:\d+}")

 

2.2  服务异常处理

  在controller中发生异常,怎样处理?

  1)自定义异常类

public class UserNotExistException extends RuntimeException {
    private static final long serialVersionId = Long.MIN_VALUE;

    private int id;

    public UserNotExistException(int id){
        super("user not exists");
        this.id = id;
    }
}

  2)处理控制器抛出的异常

@ControllerAdvice
public class ControllerExceptionHandler {
    @ExceptionHandler(UserNotExistException.class)
    @ResponseBody
    @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拦截

3.1 过滤器(Filter)

  定义过滤器,注册为spring组件

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

@Component  // 让过滤器生效, 默认拦截所有请求(/*)
public class TimeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("time filter init...");
    }

    @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);
        long finish = new Date().getTime();
        System.out.println("time filter 耗时:"+(finish-start));
        System.out.println("time filter finish");
    }

    @Override
    public void destroy() {
        System.out.println("time filter destroy...");
    }
}
TimeFilter.java

  将第三方过滤器(没有@Component)注册到项目中,将TimeFilter的@Component去掉:

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        registrationBean.setFilter(timeFilter);

        // 配置拦截的URL
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        registrationBean.setUrlPatterns(urls);
        return registrationBean;
    }
}

  在过滤器中无法获取控制器中的信息,可以使用拦截器获取spring中的控制器信息

3.2 拦截器(Interceptor)

   定义拦截器:

@Component
public class TimeInterceptor implements HandlerInterceptor {

    // 控制器方法被调用之前
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("prehandler...");
        System.out.println(((HandlerMethod)o).getBean().getClass().getName());//o为控制器方法
        System.out.println(((HandlerMethod)o).getMethod().getName());//o为控制器方法
        return true;
    }

    // 控制器方法被调用之后,如果控制器中抛出异常,则不会调用该方法
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler...");

    }

    // 处理请求后调用该方法,无论控制器方法是否抛出异常
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("after completion...");
    }
}
TimeInterceptor.java

如果配置了控制器异常处理,那么拦截器的异常对象为空。

  配置拦截器:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Autowired
    private TimeInterceptor timeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }
}

3.3 切片(Aspect)

  只需定义切面,不需要其他配置即可生效

//比较细粒度的切片,一定能获取到切入点方法的参数信息
@Aspect
@Component
public class TimeAspect {
    //切入点定义
    @Pointcut("execution(* com.getword.web.controller.UserController.*(..))")
    public void myPointcut(){}

    @Around("myPointcut()")
    public Object handlerControllerMethod(ProceedingJoinPoint pjp) throws Throwable { // 参数pjp包含控制器方法信息
        System.out.println("time aspect start...");
        //获取控制器方法的参数
        Object[] args = pjp.getArgs();
        for(Object arg : args){
            System.out.println("arg is :"+arg);
        }
        Object object = pjp.proceed();
        System.out.println("time aspect finish...");
        return object;
    }
}

 4 文件上传下载

@RestController
@RequestMapping("/file")
public class FileController {
    @PostMapping
    public FileInfo upload(MultipartFile file){
        System.out.println(file.getName());
        System.out.println(file.getOriginalFilename());
        System.out.println(file.getSize());
        try {
            InputStream inputStream = file.getInputStream();
       file.transferTo(new File("")); }
catch (IOException e) { e.printStackTrace(); } FileInfo fileInfo = new FileInfo(""); return fileInfo; } @GetMapping("/{id}") public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws Exception { //获取file文件 try(InputStream inputStream = new FileInputStream(new File("")); OutputStream out = response.getOutputStream(); ){ response.setContentType("application/x-download"); response.setHeader("Content-Disposition","attachment;filename=test.txt"); IOUtils.copy(inputStream, out); out.flush(); } } }

5 使用多线程提高RESTful性能

 5.1 使用Callable,Runnable

@RequestMapping("/order")
public Callable<String> orderAsync(){
    logger.info("主线程开始...");
    Callable<String> result = new Callable<String>() {
        @Override
        public String call() throws Exception {
            logger.info("副线程开始。。。");
            Thread.sleep(1000);
            logger.info("副线程返回。。。");
            return "success";
        }
    };
    logger.info("主线程返回...");
    return result;
}

5.2 异步处理RESTful服务,DeferredResult

1)控制器

@RequestMapping("/order3")
public DeferredResult<String> deferredResult(){
    logger.info("主线程开始");
    // 生成订单号,相当于线程号
    String orderNumber = RandomStringUtils.randomNumeric(8);
    // 处理订单,开启新的线程,同时监听是否处理完毕,如果处理完毕就对客户端响应
    mockQueue.setPlaceOrder(orderNumber);

    // 该对象可以完成向 对应客户端 做出响应
    DeferredResult<String> deferredResult = new DeferredResult<>();
    deferredResultHolder.getMap().put(orderNumber, deferredResult);

    System.out.println("主线程返回");
    return deferredResult;
}

2)订单队列

@Component
public class MockQueue {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private String placeOrder;
    private String completeOrder;

    public String getPlaceOrder() {
        return placeOrder;
    }

    public void setPlaceOrder(String placeOrder) {
        // 处理订单业务
        new Thread(()->{
            logger.info("接到下单请求..."+placeOrder);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.placeOrder = placeOrder;
            this.completeOrder = placeOrder;
            logger.info("下单请求处理完毕..."+placeOrder);
        }).start();
    }

    public String getCompleteOrder() {
        return completeOrder;
    }

    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}
MockQueue.java

3)DeferredResultHolder存放<订单序号,DeferredResult>键值对

@Component
public class DeferredResultHolder {
    // 订单号,订单结果
    private Map<String, DeferredResult<String>> map = new HashMap<>();

    public Map<String, DeferredResult<String>> getMap() {
        return map;
    }
    public void setMap(Map<String, DeferredResult<String>> map) {
        this.map = map;
    }
}

4)订单完成监听器

@Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        // 开启新的线程监听队列中是否有完成的订单
        new Thread(()->{
            while (true) {
                if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
                    // 监听到有订单完成了,结束异步请求,返回结果
                    String orderNumber = mockQueue.getCompleteOrder();
                    logger.info("返回订单处理结果:" + orderNumber);
                    deferredResultHolder.getMap().get(orderNumber).setResult("order completed success...");
                    mockQueue.setCompleteOrder(null);
                } else {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
QueueListener.java

 6 与前端并行开发

6.1 使用swagger自动生成HTML文档

1)引入maven依赖

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
View Code

2)在spring boot启动类添加模块

@SpringBootApplication
@EnableSwagger2
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

 3)api描述:

方法描述

模型参数描述:

参数描述:

6.2 使用WireMock快速伪造RESTful服务

 

 1)下载WireMock的jar包

2)运行WireMock服务器端

java -jar wiremock-standalone-2.20.0.jar --port 8000

3)使用Java代码,描述规则

public class MockServer {
    public static void main(String[] args) throws IOException {
        WireMock.configureFor(8000);
        WireMock.removeAllMappings(); //清空urlMapping

        mock("/order", "01.json");
    }
    public static void mock(String url, String file) throws IOException {
        ClassPathResource classPathResource = new ClassPathResource("mock/response/"+file);
        String content = FileUtils.readFileToString(classPathResource.getFile(), "UTF-8");
        System.out.println(content);
        WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(url))
                .willReturn(WireMock.aResponse().withBody(content)
                        .withStatus(200)));
     WireMock.saveMappings(); } }

乱码?

withHeader("content-type", "application/json;charset=utf-8")

 将mapping保存起来,下次启动WireMock时,这些mapping还存在

end

原文地址:https://www.cnblogs.com/zhuxiang1633/p/10199055.html