在请求时通过过滤器来验证请求的地址是否符合资源定义中的规则. 若符合再进行用户角色匹配, 目前支持:全匹配(一个用户有多个角色), 单匹配(一个用户只有一个角色)
<!-- Orize 注解扫描用到的依赖 -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
<!-- JAXB 的EclipseLink moxy实现 -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<!-- XPath2的实现 -->
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>10.5</version>
</dependency>
请保证使用Orize的项目中具有这些jar包。
基于SpringBoot(2.3.3. 版本无所谓了)的自动装配,示例项目: jforum2的分支:boot-orize
示例项目: jforum的分支:orize
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resources stamp="20210805">
<item>
<path>/orders/list</path>
<method>get</method>
<action>VIEW</action>
<roles>Member</roles>
<spot>do</spot>
</item>
...
</resources>
{
"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<String> parsePath(Method method) {
Stream<String> _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);
}
}
示例:
/**
* OrizeMemberQuery的实现
*/
public class OrizeMemberQueryImpl implements OrizeMemberQuery {
@Override
public OrizeMember query(HttpServletRequest request) {
//ETC
}
}
强烈不建议每次都从数据库(关系型数据库)中获取
(web.xml)示例如下:
<filter>
<filter-name>OrizeAuthServletFilter</filter-name>
<filter-class>com.apobates.forum.orize.servlet.OrizeAuthServletFilter</filter-class>
<init-param>
<param-name>loader</param-name>
<param-value>xml</param-value>
</init-param>
<init-param>
<param-name>path</param-name>
<param-value>resouces.xml</param-value>
</init-param>
<init-param>
<param-name>queryClass</param-name>
<param-value>x.y.z.OrizeMemberQueryImpl</param-value>
</init-param>
<init-param>
<param-name>roleMatch</param-name>
<param-value>any</param-value>
</init-param>
<init-param>
<param-name>ignoreDomain</param-name>
<param-value>cdn.subpu.com</param-value>
</init-param>
<init-param>
<param-name>ignorePath</param-name>
<param-value>/static/**</param-value>
</init-param>
<init-param>
<param-name>ignoreExt</param-name>
<param-value>js,css,png,svg,gif,jpg</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>OrizeAuthServletFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
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: 忽略的请求文件扩展名, 只要请求文件符合定义不进行验证. 多个之间用逗号分隔, 可选项(没有可以不设置此参数)
项目提供了一个包装类: 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<Boolean, Map> 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();
}
用户角色的验证规则只提供抽像的实现: any(OrizeMemberRoleAnyContainsPredicate),all(OrizeMemberRoleContainsAllPredicate),若这两个抽像不满足您的需求可以自行实现: OrizeMemberRolePredicater
/**
* 用户角色验证谓词表达式
*/
public interface OrizeMemberRolePredicate {
/**
* 返回验证OrizeMember的谓词表达式
*
* @return 第一个参数: OrizeMember 用户信息, 第二个参数: 资源中定义的角色要求
*/
BiPredicate<OrizeMember,String[]> getPredicate();
}
项目暂时只支持全模式匹配, 不支持通配符匹配. 例:
//资源定义: /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(){}
在项目中提供了一个示例: com.apobates.forum.orize.servlet.OrizeResourceGenTest.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。