通用权限认证要点记录
一、自定义starter
1、项目结构
2、自定义注解
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
/**
* 对方法进行描述
*/
String desc() default "";
}
@Target()
定义注解的作用目标(作用域),枚举类ElementType
包含以下:
TYPE:接口、类、枚举、注解
FIELD:字段枚举的常量
METHOD:方法PARAMETER:方法参数
CONSTRUCTOR:构造函数
LOCAL_VARIABLE:局部变量
ANNOTATION_TYPE:注解
PACKAGE:包
TYPE_PARAMETER:参数类型声明
TYPE_USE:使用的类型
@Retention()
用来指示其它注解类型保留的生命周期,枚举类RetentionPolicy
包含一下三种策略:
SOURCE:注解只会存在源代码中,将会被编译器丢弃
CLASS:注解将会保留到class文件阶段,但是在加载如vm的时候会被抛弃
RUNTIME:注解不单会被保留到class文件阶段,而且也会被vm加载进虚拟机的时候保留
3、注解处理器的依赖
<!--配置处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
spring-boot-configuration-processor
注解处理器,在编译阶段运行,一般在maven的声明中optional为 true
为自定义的配置类生成元数据信息,方便自定义配置信息
4、拦截器编写
//第一种写法 实现HandlerInterceptor接口
public class MyLogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
//第二种写法 继承HandlerInterceptorAdapter
public class MyLogInterceptor extends HandlerInterceptorAdapter{}
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();//获得被拦截的方法对象
MyLog myLog = method.getAnnotation(MyLog.class);//获得方法上的注解
5、配置拦截器
/**
* 配置类,用于自动配置拦截器、参数解析器等web组件
*/
@Configuration
public class SpringMvcConfiguration implements WebMvcConfigurer {
//注册自定义日志拦截器
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyLogInterceptor()).addPathPatterns("/**");
}
}
注意事项
如果自定义starter的包名和项目包名不一致的话,所有需要扫描的注解(
@Configuration,@Compent
)需要在XXXXAutoConfig类中使用(@Import
)导入
二、配置swagger2_3.0
- 在pom文件中添加依赖坐标:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
- 在启动类中上增加注解
@EnableOpenApi
- 根据个人情况配置一些接口例子
- 启动SpringBoot,访问swagger界面:
http://localhost:8080/swagger-ui/index.html
Failed to start bean ‘documentationPluginsBootstrapper’
错误原因:Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
三、配置Dozer
- 在pom文件中添加依赖坐标:
<dependency>
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-spring-boot-starter</artifactId>
<version>6.5.0</version>
</dependency>
- 在resources/dozer/目录下创建dozer的全局配置文件global.dozer.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://dozermapper.github.io/schema/bean-mapping"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping
http://dozermapper.github.io/schema/bean-mapping.xsd">
<!--
全局配置:
<date-format>表示日期格式
-->
<configuration>
<date-format>yyyy-MM-dd</date-format>
</configuration>
</mappings>
注:全局配置文件名称可以任意
3. 在resources/dozer/目录下创建dozer的映射文件biz.dozer.xml,也可以使用@Mapping("字段名")
的方式,不过注解只适用于简单的映射方式
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://dozermapper.github.io/schema/bean-mapping"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping
http://dozermapper.github.io/schema/bean-mapping.xsd">
<!--描述两个类中属性的对应关系,对于两个类中同名的属性可以不映射-->
<mapping date-format="yyyy-MM-dd">
<class-a>com.itheima.entity.UserEntity</class-a>
<class-b>com.itheima.dto.UserDTO</class-b>
<field>
<a>id</a>
<b>userId</b>
</field>
<field>
<a>name</a>
<b>userName</b>
</field>
<field>
<a>age</a>
<b>userAge</b>
</field>
</mapping>
<!--
可以使用map-id指定映射的标识,在程序中通过此标识来确定使用当前这个映射关系
-->
<mapping date-format="yyyy-MM-dd" map-id="user">
<class-a>com.itheima.entity.UserEntity</class-a>
<class-b>com.itheima.dto.UserDTO</class-b>
<field>
<a>id</a>
<b>userId</b>
</field>
<field>
<a>name</a>
<b>userName</b>
</field>
<field>
<a>age</a>
<b>userAge</b>
</field>
</mapping>
</mappings>
- 编写application.yml文件,将写好的映射文件添加进去
dozer:
mappingFiles:
- classpath:dozer/global.dozer.xml
- classpath:dozer/biz.dozer.xml
- 使用dozer
//加载Mapper
@Autowired
private Mapper mapper;
//使用map
mapper.map(userDTO,user,"user");
map有四种:
//Object source源对象 | Object destination目标对象 //Class<T> destinationClass对象类 | mapId映射文件中指定映射关系的id <T> T map(Object source, Class<T> destinationClass) throws MappingException; void map(Object source, Object destination) throws MappingException; <T> T map(Object source, Class<T> destinationClass, String mapId) throws MappingException; void map(Object source, Object destination, String mapId) throws MappingException;
- 对list或者set进行转换
如果直接使用map进行转换,如创建两个ArrayList<Person> plist 和 ArrayList<Student> slist
,将前者转化成后者即mapper.map(plist ,slist)
,只会用Person
替换Student
,而不是获得ArrayList<Student>
。所以想要对list,set进行转换需要对其中元素循环遍历进行转换,如下:
/**
* 将集合转成集合
* List<A> --> List<B>
*
* @param sourceList 源集合
* @param destinationClass 待转类型
* @param <T>
* @return
*/
public <T, E> List<T> mapList(Collection<E> sourceList, Class<T> destinationClass) {
if (sourceList == null || sourceList.isEmpty() || destinationClass == null) {
return Collections.emptyList();
}
List<T> destinationList = sourceList.stream()
.filter(item -> item != null)
.map((sourceObject) -> mapper.map(sourceObject, destinationClass))
.collect(Collectors.toList());
return destinationList;
}
/**
* Set<A> --> Set<B>
*/
public <T, E> Set<T> mapSet(Collection<E> sourceList, Class<T> destinationClass) {
if (sourceList == null || sourceList.isEmpty() || destinationClass == null) {
return Collections.emptySet();
}
return sourceList.stream().map((sourceObject) -> mapper.map(sourceObject, destinationClass)).collect(Collectors.toSet());
}
四、hibernate-validator后端校验
后端校验:主要是保证数据安全可靠
- hibernate-validator包含的注解
注解 | 说明 |
---|---|
@AssertTrue | 用于boolean字段,该字段只能为true |
@AssertFalse | 用于boolean字段,该字段只能为false |
@CreditCardNumber | 对信用卡号进行一个大致的验证 |
@DecimalMax | 只能小于或等于该值 |
@DecimalMin | 只能大于或等于该值 |
检查是否是一个有效的email地址 | |
@Future | 检查该字段的日期是否是属于将来的日期 |
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
@NotNull | 不能为null |
@NotBlank | 不能为空,检查时会将空格忽略 |
@NotEmpty | 不能为空,这里的空是指空字符串 |
@Pattern(regex=) | 被注释的元素必须符合指定的正则表达式 |
@URL(protocol=,host,port) | 检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件 |
- spring-boot-starter-web中已经依赖了hibernate-validator,所以不需要单独添加了
- 使用方式如下:
在对象字段上添加验证用的注解:
如:@NotNull(message = “用户id不能为空”)
@NotEmpty(message = “用户名不能为空”) @Length(max = 50, message = “用户名长度不能超过50”)
@Max(value = 80,message = “年龄最大为80”) @Min(value = 18,message = “年龄最小为18”)
@Pattern(regexp = “[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$”,message = “邮箱格式不正确”)
添加
@Validated //开启校验功能
,这个注解可以添加到类,方法,参数上,@Validated public class UserController { //简单数据类型校验 @RequestMapping("/delete") public String delete(@NotBlank(message = "id不能为空") String id){ System.out.println("delete..." + id); return "OK"; } //对象属性校验 @RequestMapping("/save") public String save(@Validated User user){ System.out.println("save..." + user); return "OK"; } }
- 创建ValidatorConfiguration类,指定校验时使用快速失败返回模式
通过控制台的输出可以看到,校验框架将我们的多个属性都进行了数据校验(默认行为),如果我们希望只要有一个属性校验失败就直接返回提示信息,后面的属性不再进行校验了该如何实现呢?
public class ValidatorConfiguration {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory =
Validation.byProvider(HibernateValidator.class)
.configure()
//快速失败返回模式
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
return validatorFactory.getValidator();
}
/**
* 开启快速返回
* 如果参数校验有异常,直接抛异常,不会进入到 controller,使用全局异常拦截进行拦截
*/
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor =
new MethodValidationPostProcessor();
/**设置validator模式为快速失败返回*/
postProcessor.setValidator(validator());
return postProcessor;
}
}
- 创建注解EnableFormValidator用于控制快速失败返回模式的开启
/**
* 在启动类上添加该注解来启动表单验证功能---快速失败返回模式
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ValidatorConfiguration.class)
public @interface EnableFormValidator {
}
- 在启动类上加入EnableFormValidator注解,开启快速失败返回模式
@SpringBootApplication
@EnableFormValidator
public class HibernateValidatorApp {
public static void main(String[] args) {
SpringApplication.run(HibernateValidatorApp.class,args);
}
}
五、防跨站脚本攻击(XSS)
XSS介绍
XSS:跨站脚本攻击(Cross Site Scripting),为不和 CSS混淆,故将跨站脚本攻击缩写为XSS。XSS是指恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。有点类似于sql注入。
XSS攻击原理:
HTML是一种超文本标记语言,通过将一些字符特殊地对待来区别文本和标记,例如,小于符号(<)被看作是HTML标签的开始,之间的字符是页面的标题等等。当动态页面中插入的内容含有这些特殊字符时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。所以,当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
AntiSamy介绍
AntiSamy是OWASP的一个开源项目,通过对用户输入的 HTML / CSS / JavaScript 等内容进行检验和清理,确保输入符合应用规范。AntiSamy被广泛应用于Web服务对存储型和反射型XSS的防御中。
AntiSamy使用
- 添加maven坐标
<dependency>
<groupId>org.owasp.antisamy</groupId>
<artifactId>antisamy</artifactId>
</dependency>