# jforum.orize
**Repository Path**: subpu/jforum.orize
## Basic Information
- **Project Name**: jforum.orize
- **Description**: 请求地址验证.
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-08-08
- **Last Updated**: 2021-09-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# jforum.orize
#### 介绍
在请求时通过过滤器来验证请求的地址是否符合资源定义中的规则. 若符合再进行用户角色匹配, 目前支持:全匹配(一个用户有多个角色), 单匹配(一个用户只有一个角色)
#### 安装教程
##### 1. 项目基于jdk8开发. 低于这个版本无法运行。
##### 2. 项目依赖(Maven)
```
org.reflections
reflections
0.9.12
org.eclipse.persistence
org.eclipse.persistence.moxy
2.7.1
javax.xml.bind
jaxb-api
2.2.11
net.sf.saxon
Saxon-HE
10.5
```
请保证使用Orize的项目中具有这些jar包。
##### 3. SpringBoot使用超容易:[jforum.orize.starter](https://gitee.com/subpu/jforum.orize.starter)
基于SpringBoot(2.3.3. 版本无所谓了)的自动装配,示例项目: [jforum2的分支:boot-orize](https://gitee.com/subpu/jforum2/tree/boot-orize/)
##### 4. 非SpringBoot的SpringMVC
示例项目: [jforum的分支:orize](https://gitee.com/subpu/jforum/tree/orize/)
#### 使用说明
##### 1. 定义需要验证的资源. 目前支持:xml,json,注解。资源定义(xml/json)文件需要放在WEB-INF的目录下
###### xml结构如下:
```
-
/orders/list
get
VIEW
Member
do
...
```
###### json结构如下:
```
{
"resources" : {
"stamp" : "20210805",
"item" : [ {
"path" : "/orders/list",
"method" : "get",
"action" : "VIEW",
"roles" : "Member",
"spot" : "do"
}, ...
]
}
}
```
###### 注解:
需要在负责完成请求的方法上增加注解: Orize,例:
```
@GetMapping(path="/{path}.xhtml")
@Orize(roles={"GUEST", "ADMIN", "MEMBER"}, method="GET", action={OrizeAction.VIEW})
public String boardHome(){}
```
注意:当一个方法同时负责新增和编辑操作时注解需要如下使用
```
@PostMapping(path = "/edit")
@Orize(roles={"GUEST", "ADMIN", "MEMBER"}, method="POST", action={OrizeAction.ADD, OrizeAction.EDIT}, spot="action")
public String editBoardPage(){}
```
上面的示例表示新增时地址如下:
```
/edit?action=add
```
编辑时地址如下:
```
/edit?action=edit
```
以此来达到区分同一个请求路径表示哪个具体的操作. 若一个地址只完成一个操作不需要使用spot查询参数Key,若spot的值等于do可以忽略(默认值)
扫描注解(Orize)资源定义需要实现: OrizeAnnotationScanner, 以下示例为SpringMVC的实现
```
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class OrizeAnnotationSpringScanner extends OrizeAnnotationScanner {
@Override
protected List parsePath(Method method) {
Stream _methodPath = Stream.empty(), _classPath = Stream.empty();
GetMapping getAnno = method.getAnnotation(GetMapping.class);
if(null != getAnno){
String[] getMethodPath = getAnno.path();
String[] getMethodValue = getAnno.value();
_methodPath = Stream.concat(Stream.of(getMethodPath), Stream.of(getMethodValue));
}
PostMapping postAnno = method.getAnnotation(PostMapping.class);
if(null != postAnno){
String[] postMethodPath = postAnno.path();
String[] postMethodValue = postAnno.value();
_methodPath = Stream.concat(Stream.of(postMethodPath), Stream.of(postMethodValue));
}
//
Class> aClass = method.getDeclaringClass();
RequestMapping _crm = aClass.getAnnotation(RequestMapping.class);
if(null != _crm){
String[] classPathPrefix = _crm.path();
String[] classValPrefix = _crm.value();
_classPath = Stream.concat(Stream.of(classPathPrefix), Stream.of(classValPrefix));
}
return OrizeAnnotationScanner.cartesian(
_methodPath.collect(Collectors.toList()),
_classPath.collect(Collectors.toList()),
OrizeAnnotationScanner.mergePathFun);
}
}
```
##### 2. 实现用户信息查询接口: OrizeMemberQuery
示例:
```
/**
* OrizeMemberQuery的实现
*/
public class OrizeMemberQueryImpl implements OrizeMemberQuery {
@Override
public OrizeMember query(HttpServletRequest request) {
//ETC
}
}
```
强烈不建议每次都从数据库(关系型数据库)中获取
##### 3. 配置过滤器
(web.xml)示例如下:
```
OrizeAuthServletFilter
com.apobates.forum.orize.servlet.OrizeAuthServletFilter
loader
xml
path
resouces.xml
queryClass
x.y.z.OrizeMemberQueryImpl
roleMatch
any
ignoreDomain
cdn.subpu.com
ignorePath
/static/**
ignoreExt
js,css,png,svg,gif,jpg
OrizeAuthServletFilter
/*
```
init-param说明:
loader: 支持:xml,json,anno; 根据值不同使用不同的资源加载器
path:资源所在的位置,需要放到WEB-INF目录下
queryClass:OrizeMemberQuery实现类的类全名, 实现类中需要有无参的构造器
roleMatch:用户角色的匹配规则, any(单角色匹配),all(多角色匹配)
ignoreDomain: 忽略的域名, 只要请求来自这些域名不进行验证. 多个之间用逗号分隔, 可选项(没有可以不设置此参数) 支持以下几种模式:
```
*.a.com, a.com, cdn.a.com
```
ignorePath: 忽略的请求路径, 只要请求路径符合定义不进行验证. 多个之间用逗号分隔, 可选项(没有可以不设置此参数) 支持以下几种模式:
```
/static, /static/*, /static/*/*.css, /static/**
```
ignoreExt: 忽略的请求文件扩展名, 只要请求文件符合定义不进行验证. 多个之间用逗号分隔, 可选项(没有可以不设置此参数)
#### 其它说明
##### 1. SpringMVC不建议使用OrizeAuthServletFilter.
项目提供了一个包装类: com.apobates.forum.member.strategy.spring.OrizeAuthHelper
使用示例如下:
```
/**
* 实现Orize过滤器
*/
public class OrizeAuthSpringInterceptorImpl extends HandlerInterceptorAdapter {
@Autowired
private OrizeAuthHelper orizeAuthHelper;
@Autowired
private OrizeMemberRolePredicate memberRolePredicate;
@Value("${site.orize.log}")
private String orizeResultLog;
/**
* 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*
* @param request
* @param response
* @param handler
* @return
* @throws java.lang.Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ImmutablePair ips = orizeAuthHelper.verify(memberRolePredicate, request);
if(!ips.getLeft()){
String redirectPath = request.getContextPath() + orizeResultLog; // 验证失败时的提示地址;
FlashMap flashMap = new FlashMap();
flashMap.put("errors", ips.getRight().get("message"));
flashMap.put("method", ips.getRight().get("reqMethod"));
flashMap.put("path", ips.getRight().get("reqPath"));
flashMap.setTargetRequestPath(redirectPath);
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, response);
response.sendRedirect(redirectPath);
return false;
}
return true;
}
//ETC
}
```
SpringMVC的忽略参数设置:
```
//不需要验证的配置
@Bean(name="cusIgnoreConfig")
public OrizeAuthIgnoreConfig getIgnoreConfig(){
return OrizeAuthIgnoreConfig
.defaultInstance()
.setIgnoreMediaType("js", "css", "png", "svg", "gif", "jpg");
}
//资源验证助手类
@Bean(name="orizeAuthHelper")
public OrizeAuthHelper getAuthHelper(@NotNull OrizeMemberQuery memberQuery, @Nullable OrizeAuthIgnoreConfig cusIgnoreConfig, ServletContext sc){
return OrizeAuthHelper
.defaultInstance("anno", sc)
.setMemberQuery(memberQuery)
.setIgnoreConfig(cusIgnoreConfig)
.build();
//手动生成资源定义
//return OrizeAuthHelper.defaultInstance("xml", sc).setNotAnnoResourcePath("resources.xml").setMemberQuery(memberQuery).setIgnoreConfig(cusIgnoreConfig).build();
}
```
##### 2. 项目不提供角色定义。
用户角色的验证规则只提供抽像的实现: any(OrizeMemberRoleAnyContainsPredicate),all(OrizeMemberRoleContainsAllPredicate),若这两个抽像不满足您的需求可以自行实现: OrizeMemberRolePredicater
```
/**
* 用户角色验证谓词表达式
*/
public interface OrizeMemberRolePredicate {
/**
* 返回验证OrizeMember的谓词表达式
*
* @return 第一个参数: OrizeMember 用户信息, 第二个参数: 资源中定义的角色要求
*/
BiPredicate getPredicate();
}
```
##### 3. 关于资源定义中的路径包含占位符
项目暂时只支持全模式匹配, 不支持通配符匹配. 例:
```
//资源定义: /board/volumes/{path}.xhtml
//匹配请求地址
/board/volumes/20210810.xhtml
//不匹配请求地址
/board/2020/20210810.xhtml
```
为了支持占位符Orize注解增加两个新方法
```
/**
* 注解.用于收集资源的定义
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Orize {
/**
* 路径中是否存在占位符
* 例:
* /{id}.xhtml 存在
* /edit 不存在
* @return true存在/false不存在
*/
boolean slot()default false;
/**
* 若存在占位符,占位的名称是什么
* 例:/{id}.xhtml, slotKeys={"id"}
* @return
*/
String[] slotKeys()default {"*"};
//ETC
}
```
SpringMVC控制器方法示例:
```
@GetMapping(path="/{path}.xhtml")
@Orize(roles={"NO","BM","MASTER","ADMIN"}, method="get", action={OrizeAction.VIEW}, slot = true, slotKeys = {"path"})
public String volumeHome(){}
```
##### 4. 关于xml和json文件的生成
在项目中提供了一个示例: com.apobates.forum.orize.servlet.OrizeResourceGenTest.