《Java后端安全开发Spring Security开发REST服务》1-3章——基本使用

1. 过滤器

一共三种:

1.1 Filter

1.1.1 两种引入方式

  1. @Component注解到类上,自动引入
package com.wxm.spring.security.demo.web.filter;

import java.io.IOException;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.stereotype.Component;

@Component
public class TimeFilter implements Filter {

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("time filter start");
		long start = new Date().getTime();
		chain.doFilter(request, response);
		System.out.println("time filter 耗时:"+(new Date().getTime()-start));
		System.out.println("time filter finish");
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException{
		System.out.println("time filter init");
	}
	
	@Override
	public void destroy(){
		System.out.println("time filter destory");
	}
}

  1. WebMvcConfigurer中用@Bean注解引入(详见WebConfig)

1.1.2 要点

  1. 当使用第三方类库中的Filter时,有可能没办法自己添加@Component注解,所以用这种方式实现引入
  2. 只能实现调用前的过滤:doFilter

1.2 HandlerInterceptor

1.2.1 要点

  1. 不同于Filter,仅仅添加Component不能使此拦截器生效,还需要在WebMvcConfigurer.WebMvcConfigurer中配置一下才会生效,详见WebConfig配置类
@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(timeInterceptor);
//		注册TimeInterceptor拦截器
	}
  1. 可以在preHandle中向HttpServletRequest中添加属性,例如:request.setAttribute(“startTime”, new Date().getTime());
  2. 然后在postHandle中获取,例如:long start = (long)request.getAttribute(“startTime”);
  3. 能获取到ServletRequest和ServletResponse
  4. 能对调用的方法实现包围:preHandle、postHandle、afterCompletion
  5. Controller中抛出异常时,就不会调用postHandler方法,但会调用afterCompletion,所有异常都会先被Filter捕获
  6. 如果异常被ControllerExceptionHandler捕获,该异常将被消耗掉,那么此处的ex将为null:
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

1.2.1 实例

/**
 * 
 */
package com.wxm.spring.security.demo.web.interceptor;

import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.ibatis.javassist.expr.NewArray;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author wxm
 *
 */
@Component
//不同于Filter,仅仅添加Component不能使此拦截器生效,还需要在WebMvcConfigurer.WebMvcConfigurer中配置一下才会生效,详见WebConfig配置类
public class TimeInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("TimeInterceptor preHandler");
		//对应控制器类名
		try {
			System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
			//对应控制器方法名
			System.out.println(((HandlerMethod)handler).getMethod().getClass().getName());
		}catch(ClassCastException e) {
			
		}
		
		request.setAttribute("startTime", new Date().getTime());
		//return HandlerInterceptor.super.preHandle(request, response, handler);
		return true;
	}

	//Controller中抛出异常时,就不会调用postHandler方法,但会调用afterCompletion
	//所有异常都会先被Filter捕获
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("TimeInterceptor postHandle");
		long start = (long)request.getAttribute("startTime");
		System.out.println("TimeInterceptor 耗时:"+(new Date().getTime()-start));
		//HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
	}

	@Override
	//如果异常被ControllerExceptionHandler捕获,该异常将被消耗掉,那么此处的ex将为null
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("TimeInterceptor afterCompletion");
		
		/*
		Enumeration<String> attributeNames = request.getAttributeNames();
		Iterator<String>iterator =  attributeNames.asIterator();
		while(iterator.hasNext()) {
			String name = iterator.next();
			System.out.println("Attribute: "+name+" "+request.getAttribute(name));
		}
		*/
		
		Long start = (Long)request.getAttribute("startTime");
		System.out.println("TimeInterceptor 耗时:"+(new Date().getTime()-start));
		System.out.println("ex is "+ex);
		//HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
	}

}

1.3 切片 @Aspect

  1. @Aspect+@Component实现自动引入
  2. 需要spring.aop库
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-aop</artifactId>
			</dependency>
  1. 能获取Controller、方法、返回对象,获取不到ServletRequest和ServletResponse
    例如:
    // pjp其实就是对应UserController,pjp.proceed()就是调用UserController的对应方法,Object 对应返回对象
    // 以UserCongroller中:public User getInfo(@PathVariable(name = “id” ) String id)方法为例
    // pjp => UserController
    // pjp.proceed() => UserController.getInfo
    // object => User
package com.wxm.spring.security.demo.web.aspect;

import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimeAspect {

	@Around("execution(* com.wxm.spring.security.demo.web.controller.UserController.*(..))")
	public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("TimeAspect start");
		long start = new Date().getTime();

		Object[] args = pjp.getArgs();
		for(Object arg : args) {
			System.out.println("arg is "+arg);
		}
//		pjp其实就是对应UserController,pjp.proceed()就是调用UserController的对应方法,Object 对应返回对象
//		以UserCongroller中:public User getInfo(@PathVariable(name = "id" ) String id)方法为例
//		pjp				=>	UserController
//		pjp.proceed()	=>	UserController.getInfo
//		object			=>	User
		Object object = pjp.proceed();

		System.out.println("TimeInterceptor 耗时:"+(new Date().getTime()-start));
		System.out.println("TimeAspect end");
		return object;
	}
}

1.4 异步拦截器

多线程需要单独的拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
	public void configureAsyncSupport(AsyncSupportConfigurer configure) 
		//configure.registerCallableInterceptors(interceptors):配置Callable对应的拦截器
		//configure.registerDeferredResultInterceptors(interceptors):配置DeferredResult对应的拦截器
		//configure.setDefaultTimeout(timeout):设置超时时间
		//configure.setTaskExecutor(taskExecutor):设置自定义实现的线程池,代替spring默认线程池(无线程回收复用)
	}
}

1.5 @ControllerAdvice

@ControllerAdvice用途:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理

2. 全局异常处理

@ControllerAdvice
//@ControllerAdvice用途:
//全局异常处理
//全局数据绑定
//全局数据预处理
package com.wxm.spring.security.demo.web.controller;

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

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 com.wxm.spring.security.demo.exception.UserNotExistException;
public class ControllerExceptionHandler {
	@ExceptionHandler(UserNotExistException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//	捕获UserNotExistException异常
//	返回HashMap
//	返回Http异常为:服务器内部错误500(HttpStatus.INTERNAL_SERVER_ERROR)
	public Map<String,Object> handleUserNotExistException(UserNotExistException ex){
		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("id", ex.getId());
		resultMap.put("message", ex.getMessage());
		return resultMap;
	}
}

3. 自动化检测

3.1 hamcrest

3.1.1 库依赖

需要引入org.hamcrest库

		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest</artifactId>
		</dependency>

3.1.2 使用

两步:

  1. 数据对象中引入标注:
    @NotBlank(message = “密码不能为空”)
    @Past(message = “生日必须是以前的时间”) //判断:过去时间
public class User {
	private int id;
	@NotBlank(message = "密码不能为空")
	private String password;
	@Past(message = "生日必须是以前的时间")	//判断:过去时间
	private Date birthday;
	.
	.
	.
}
  1. Controller中引入标注:
@RestController
public class UserController {
	@PostMapping("/user")
	//@Valid和User中的@NotBlank联用,用于参数校验
	//BindingResult,@Valid验证失败时,将错误信息放入BindingResult,此时本方法将不会报错
	public User create(@Valid @RequestBody User user,BindingResult errors) {	
		if(errors.hasErrors()) {
			errors.getAllErrors().stream().forEach(error->System.out.println(error.getDefaultMessage()));
		}
		
		user.setId(1);
		
		System.out.println(user);
		
		return user;
	}
	.
	.
	.
}

3.2 @JsonView

3.2.1 使用

  1. 声明视图接口,例如:UserSimpleView、UserDetailView
  2. 用@JsonView标注,将接口配置到数据对象的方法上,例如:@JsonView(UserSimpleView.class)
  3. 用@JsonView标注,将接口配置到Controller的方法上,例如:@JsonView(UserSimpleView.class)
public class User {
	public interface UserSimpleView{};
	public interface UserDetailView extends UserSimpleView{};
	.
	.
	.
	@JsonView(UserSimpleView.class)
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	@JsonView(UserDetailView.class)
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	@JsonView(UserSimpleView.class)
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	@JsonView(UserSimpleView.class)
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	.
	.
	.
}
@RestController
public class UserController {
	@GetMapping("/user")
	@JsonView(User.UserSimpleView.class)	//定义视图,是否隐藏密码字段
	public List<User> query(@RequestParam String username){
		List<User>users = new ArrayList<>();
		users.add(new User());
		users.add(new User());
		users.add(new User());
		return users;
	}
	.
	.
	.
}

3.3 自定义注解

3.2.1 使用

  1. 创建注解类
@Target({ ElementType.METHOD,ElementType.FIELD })
//@Taraget表明本注解可以标注在什么元素上(类、方法)
@Retention(RetentionPolicy.RUNTIME)
//注解@Retention可以用来修饰注解,是注解的注解,称为元注解。
//Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型,
//这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS  RUNTIME   SOURCE
//按生命周期来划分可分为3类:
//1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
//2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
//3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Constraint(validatedBy = MyConstraintValidator.class)
//MyConstraintValidator是校验器,执行校验逻辑的类
//MyConstraint是校验注解
public @interface MyConstraint {
	//message、groups、Payload是必须有的内容,message返回校验报错信息
	String message() default "{javax.validation.constraints.NotBlank.message}";

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

	Class<? extends Payload>[] payload() default { };
}
  1. 创建验证器
package com.wxm.spring.security.demo.validator;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import com.wxm.spring.security.demo.service.HelloService;

public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object>{
	@Autowired
	private HelloService HelloService;
	
	@Override
	public void initialize(MyConstraint constraintAnnotation) {
		System.out.println("my validator init");
	}
	
	@Override
	public boolean isValid(Object value, ConstraintValidatorContext context) {
		HelloService.greeting("tom");
		System.out.println(value);
		return true;
	}

}

  1. 在数据对象上标注
public class User {
	@MyConstraint(message = "@MyConstraint是自建的校验注解")
	//@MyConstraint是自建的校验注解
	private String username;
	.
	.
	.
}

4. 多线程

4.1 2种实现方式

  1. 使用Callable接口
  2. 使用DeferrdResult类

4.2 Callable接口方式

@RestController
public class AsyncController {
@RequestMapping("/orderAsync")
	public Callable<String> orderAsync() throws InterruptedException {
		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;
	}
}

4.3 DeferrdResult类方式

  1. Controller
@RestController
public class AsyncController {
	@Autowired
	private MockQueue mockQueue;
	@Autowired
	private DeferredResultHolder deferredResultHolder;
	
	@RequestMapping("/orderAsync")
	@RequestMapping("/orderAsyncWithDeferredResult")
	public DeferredResult<String> orderAsyncWithDeferredResult() throws InterruptedException {
		logger.info("主线程开始");
		String orderNumber = RandomStringUtils.random(8);
		mockQueue.setPlaceOrder(orderNumber);
		DeferredResult<String> result = new DeferredResult<>();
		deferredResultHolder.getMap().put(orderNumber, result);
		logger.info("主线程返回");
		
		return result;
	}
}
  1. 实现工作队列:DeferredResultHolder、MockQueue
package com.wxm.spring.security.demo.web.async;

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


import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;


@Component
public class DeferredResultHolder {
	private Map<String, DeferredResult<String>> map = new HashMap<String, DeferredResult<String>>();

	public Map<String, DeferredResult<String>> getMap() {
		return map;
	}

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

package com.wxm.spring.security.demo.web.async;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@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) {
				// TODO Auto-generated catch block
				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;
	}
	
	@Override
	public String toString() {
		return "MockQueue [placeOrder=" + placeOrder + ", completeOrder=" + completeOrder + "]";
	}
}

  1. 模拟工作队列处理服务:QueueListener
package com.wxm.spring.security.demo.web.async;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
	@Autowired
	private MockQueue mockQueue;
	@Autowired
	private DeferredResultHolder deferredResultHolder;
	
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		new Thread(()-> {
			while(true) {
				if(StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
					String orderNumber = mockQueue.getCompleteOrder();
					logger.info("返回订单处理结果: "+orderNumber);
					deferredResultHolder.getMap().get(orderNumber).setResult("place order success");
					mockQueue.setCompleteOrder(null);
				}else {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}).start();
	}
}

5. 自动生成文档:Swagger

5.1 依赖包

			<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
			<dependency>
			    <groupId>io.springfox</groupId>
			    <artifactId>springfox-swagger2</artifactId>
			    <version>2.9.1</version>
			</dependency>
		  	<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
			<dependency>
			    <groupId>io.springfox</groupId>
			    <artifactId>springfox-swagger-ui</artifactId>
			    <version>2.9.1</version>
			</dependency>

使用

  1. 主程序类中配置
package com.wxm.spring.security.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
public class DemoApplication {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SpringApplication.run(DemoApplication.class, args);
	}
	
}
  1. 添加标注
@RestController
public class UserController {
	@PostMapping("/user")
	//Swagger2注解
	@ApiOperation(value = "POST: 创建用户")	
	public User create(@Valid @RequestBody User user,BindingResult errors) {	
		.
		.
		.
	}
	@DeleteMapping("/user/{id:\\d+}")
	//@ApiParam:Swagger2注解
	public void delete(@ApiParam(value = "用户id") @PathVariable String id) {	
		System.out.println("DELETE: "+id);

	}
	.
	.
	.
}
  1. 访问网页
    http://localhost:8080/swagger-ui.html

6. maven父子项目

  1. wxm-spring-security: pom.xml
<?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 https://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.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wxm.spring.security</groupId>
	<artifactId>wxm-spring-security</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<name>wxm-spring-security</name>
	<description>wxm-spring-security</description>
	<packaging>pom</packaging>
	
	<properties>
		<java.version>1.8</java.version>
		<wxm.security.version>1.0.0-SNAPSHOT</wxm.security.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter</artifactId>
			</dependency>
			<dependency>
			    <groupId>org.springframework.cloud</groupId>
			    <artifactId>spring-cloud-dependencies</artifactId>
			    <version>Hoxton.SR1</version>
			    <type>pom</type>
			    <scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-test</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>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

  1. wxm-spring-security-core: pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.wxm.spring.security</groupId>
		<artifactId>wxm-spring-security</artifactId>
		<version>1.0.0-SNAPSHOT</version>
		<relativePath>../wxm-spring-security</relativePath> <!-- lookup parent from repository -->
	</parent>
	<artifactId>wxm-spring-security-core</artifactId>
	<name>wxm-spring-security-core</name>
	<description>wxm-spring-security-core</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>


		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-web</artifactId>
			</dependency>
			<dependency>
				<groupId>org.mybatis.spring.boot</groupId>
				<artifactId>mybatis-spring-boot-starter</artifactId>
				<version>2.1.1</version>
			</dependency>
			
			<dependency>
		  		<groupId>org.springframework.boot</groupId>
		  		<artifactId>spring-boot-starter-jdbc</artifactId>
		  	</dependency>

			<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<scope>runtime</scope>
			</dependency>
			
			
			<dependency>
		  		<groupId>org.springframework.boot</groupId>
		  		<artifactId>spring-boot-starter-data-redis</artifactId>
		  	</dependency>
		  	<!-- 
		  	
		  	

			-->
			<dependency>
			    <groupId>org.springframework.cloud</groupId>
			    <artifactId>spring-cloud-starter-oauth2</artifactId>
			</dependency>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-context</artifactId>
			</dependency>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-webmvc</artifactId>
			</dependency>
			<dependency>
				<groupId>commons-lang</groupId>
				<artifactId>commons-lang</artifactId>
				<version>2.6</version>
			</dependency>
			<dependency>
				<groupId>commons-collections</groupId>
				<artifactId>commons-collections</artifactId>
				<version>3.2.2</version>
			</dependency>
			<dependency>
				<groupId>commons-beanutils</groupId>
				<artifactId>commons-beanutils</artifactId>
				<version>1.9.3</version>
			</dependency>
			<dependency>
				<groupId>org.springframework.social</groupId>
				<artifactId>spring-social-config</artifactId>
				<version>1.1.6.RELEASE</version>
			</dependency>
		  	<dependency>
		  		<groupId>org.springframework.social</groupId>
		  		<artifactId>spring-social-core</artifactId>
		  		<version>1.1.6.RELEASE</version>
		  	</dependency>
		  	<dependency>
		  		<groupId>org.springframework.social</groupId>
		  		<artifactId>spring-social-security</artifactId>
		  		<version>1.1.6.RELEASE</version>
		  	</dependency>
		  	<dependency>
		  		<groupId>org.springframework.social</groupId>
		  		<artifactId>spring-social-web</artifactId>
		  		<version>1.1.6.RELEASE</version>
		  	</dependency>


		  	<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-context</artifactId>
			</dependency>
			
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-test</artifactId>
				<version>2.2.4.RELEASE</version>
			</dependency>
			<!-- 
		  	<dependency>
		  		<groupId>org.junit.jupiter</groupId>
		  		<artifactId>junit-jupiter</artifactId>
		  	</dependency>
		  	<dependency>
		  		<groupId>org.junit.jupiter</groupId>
		  		<artifactId>junit-jupiter-api</artifactId>
		  	</dependency>
		  	<dependency>
		  		<groupId>org.junit.jupiter</groupId>
		  		<artifactId>junit-jupiter-engine</artifactId>
		  	</dependency>
		  	<dependency>
		  		<groupId>org.junit.jupiter</groupId>
		  		<artifactId>junit-jupiter-params</artifactId>
		  	</dependency>
		  	 -->
		  	<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-aop</artifactId>
			</dependency>
		  	<dependency>
		  		<groupId>commons-io</groupId>
		  		<artifactId>commons-io</artifactId>
		  		<version>2.5</version>
		  	</dependency>
		  	<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
			<dependency>
			    <groupId>io.springfox</groupId>
			    <artifactId>springfox-swagger2</artifactId>
			    <version>2.9.1</version>
			</dependency>
		  	<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
			<dependency>
			    <groupId>io.springfox</groupId>
			    <artifactId>springfox-swagger-ui</artifactId>
			    <version>2.9.1</version>
			</dependency>
		  	
		</dependencies>

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

</project>

  1. wxm-spring-security-app: pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.wxm.spring.security</groupId>
		<artifactId>wxm-spring-security</artifactId>
		<version>1.0.0-SNAPSHOT</version>
		<relativePath>../wxm-spring-security</relativePath> <!-- lookup parent from repository -->
	</parent>
	<artifactId>wxm-spring-security-app</artifactId>
	<name>wxm-spring-security-app</name>
	<description>wxm-spring-security-app</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
	  		<groupId>com.wxm.spring.security</groupId>
	  		<artifactId>wxm-spring-security-core</artifactId>
	  		<version>${wxm.security.version}</version>
	  	</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
		
	</dependencies>

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

</project>

  1. wxm-spring-security-browser: pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.wxm.spring.security</groupId>
		<artifactId>wxm-spring-security</artifactId>
		<version>1.0.0-SNAPSHOT</version>
		<relativePath>../wxm-spring-security</relativePath> <!-- lookup parent from repository -->
	</parent>
	<artifactId>wxm-spring-security-browser</artifactId>
	<name>wxm-spring-security-browser</name>
	<description>wxm-spring-security-browser</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>


	<dependencies>
		<dependency>
	  		<groupId>com.wxm.spring.security</groupId>
	  		<artifactId>wxm-spring-security-core</artifactId>
	  		<version>${wxm.security.version}</version>
	  	</dependency>
	  	<dependency>
	      <groupId>org.springframework.session</groupId>
	      <artifactId>spring-session-bom</artifactId>
	      <version>Bean-SR8</version>
	      <type>pom</type>
	      <scope>compile</scope>
	    </dependency>
	    <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>
	</dependencies>

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

</project>

  1. wxm-spring-security-demo: pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.wxm.spring.security</groupId>
		<artifactId>wxm-spring-security</artifactId>
		<version>1.0.0-SNAPSHOT</version>
		<relativePath>../wxm-spring-security</relativePath> <!-- lookup parent from repository -->
	</parent>
	<artifactId>wxm-spring-security-demo</artifactId>
	<name>wxm-spring-security-demo</name>
	<description>wxm-spring-security-demo</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
	  		<groupId>com.wxm.spring.security</groupId>
	  		<artifactId>wxm-spring-security-browser</artifactId>
	  		<version>${wxm.security.version}</version>
	  	</dependency>
		<dependency>
	  		<groupId>com.wxm.spring.security</groupId>
	  		<artifactId>wxm-spring-security-app</artifactId>
	  		<version>${wxm.security.version}</version>
	  	</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>


		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest</artifactId>
		</dependency>
		
	</dependencies>

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

</project>

7. 自动化测试

package com.wxm.spring.security.demo;

import static org.springframework.test.web.client.match.MockRestRequestMatchers.queryParam;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.net.URI;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
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 org.springframework.web.multipart.MultipartFile;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private WebApplicationContext wac;
	
	private MockMvc mockMvc;
	
	@Before
	public void setup() {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
	}
	
	@Test
	public void whenQuerySuccess() throws Exception {
		String result = mockMvc.perform(get("/user")
			.param("username", "wxm")
			.contentType(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.length()").value(3))
			.andReturn().getResponse().getContentAsString();
				
		System.out.print(result);		
	}
	
	@Test
	public void whenQuerySuccess2() throws Exception {
		mockMvc.perform(get("/user2")
			.param("username", "wxm")
			.param("age", "18")
			.param("ageTo", "180")
			.contentType(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.length()").value(3));
				
				
	}
	
	@Test
	public void whenQuerySuccess3() throws Exception {
		String result = mockMvc.perform(get("/userPage")
			.param("username", "wxm")
			.param("age", "18")
			.param("ageTo", "180")
			.param("size", "15")		//每页15条数据
			.param("page","3")			//查第3页
			//.param("sort","age,desc")
			.param("sort","id,desc")
			.contentType(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.length()").value(3))
			.andReturn().getResponse().getContentAsString();
		logger.info(result);
	}
	
	@Test
	public void wheGetInfoSucess() throws Exception {
		String result = mockMvc.perform(get("/user/1")
			.contentType(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.username").value("tom"))
			.andReturn().getResponse().getContentAsString();
		
		System.out.println(result);		
	}
	
	@Test
	public void whenGetInfoFail()  throws Exception {
		mockMvc.perform(get("/user2/a")
				.contentType(MediaType.APPLICATION_JSON))
		.andExpect(status().is4xxClientError());	//return 4xx error
	}
	
	@Test
	public void whenCreateSuccess() throws Exception {
		Date date = new Date();
		
		String content = "{\"username\":\"tom\",\"password\":\"myPassword\",\"birthday\":"+date.getTime()+"}";
		String result = mockMvc.perform(post("/user")
				.contentType(MediaType.APPLICATION_JSON)
				.content(content))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.id").value("1"))
			.andReturn().getResponse().getContentAsString();
		logger.info(result);
		//logger.info("_________________________________");
	}
	
	@Test
	public void whenUpdateSuccess() throws Exception {
		Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());

		String content = "{\"id\":\"1\",\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}";
		String result = mockMvc.perform(put("/user/1")
				.contentType(MediaType.APPLICATION_JSON)
				.content(content))
			.andExpect(status().isOk())	//return 200
			.andExpect(jsonPath("$.id").value("1"))
			.andReturn().getResponse().getContentAsString();
		System.out.println(result);
	}
	
	@Test
	public void whenDeleteSuccess() throws Exception {
		mockMvc.perform(delete("/user/1")
				.contentType(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk());	//return 200
	}
	
	@Test
	public void whenUploadSuccess() throws Exception{
		//MockMultipartFile中的第一个参数"file"要与FileController.upload(MultipartFile file)中的file一致
		mockMvc.perform(fileUpload("/file")
				.file(new MockMultipartFile("file", "test.txt","multipart/form-data","hello upload".getBytes("UTF-8"))))
		.andExpect(status().isOk());	//return 200
	}
}

8. WireMock

8.1 依赖库

			<dependency>
			    <groupId>com.github.tomakehurst</groupId>
			    <artifactId>wiremock</artifactId>
			    <version>2.26.0</version>
			</dependency>

8.2 使用

  1. 下载wiremock-standalone-2.26.0.jar,官网
  2. 控制台启动服务:java -jar ./wiremock-standalone-2.26.0.jar --port 8062
  3. 便携服务代码
package com.wxm.spring.security.demo.wiremock;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.ClassPathResource;

import com.github.tomakehurst.wiremock.client.WireMock;

public class MockServer {
	public static void main(String[] args) throws IOException {
		configureFor(8062);
		removeAllMappings();//清空所有配置
		
		mock("/order/1", "01");
		/*
		 ClassPathResource resource = new ClassPathResource("mock/response/01.txt");
		String content = StringUtils.join(FileUtils.readLines(resource.getFile(),"UTF-8").toArray(),"\n");
		stubFor(get(urlPathEqualTo("/order/1"))
				.willReturn(aResponse().withBody(content)
						.withStatus(200)
						//.withHeader(key, values)...
						)
				);
		 */
		/*
		stubFor(get(urlPathEqualTo("/order/1"))
			.willReturn(aResponse().withBody("{\"id\":1}")
					.withStatus(200)
					//.withHeader(key, values)...
					)
			);
		*/
	}

	private static void mock(String url,String file) throws IOException {
		ClassPathResource resource = new ClassPathResource("mock/response/"+file+".txt");
		String content = StringUtils.join(FileUtils.readLines(resource.getFile(),"UTF-8").toArray(),"\n");
		stubFor(get(urlPathEqualTo(url))
				.willReturn(aResponse().withBody(content)
						.withStatus(200)
						//.withHeader(key, values)...
						)
				);
	}
	
}

  1. 创建目录:/src/main/resources/mock/response,创建01.txt文件:(MockServer 读取01.txt中的内容返回)
{
	"id":1,
	"type":"A"
}
  1. 运行MockServer
  2. 浏览器访问:http://localhost:8062/order/1,将返回01.txt中的内容

9. 其他

9.1 分页

@PageableDefault(page = 2,size = 17,sort = “username,asc”) Pageable pageable

9.2 restlet client

9.3 多模块时,自动注入会有问题

https://blog.csdn.net/machuang30508/article/details/78616501

9.4 OpenFeign

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值