安全相关
跨站伪造请求攻击防护(CSRF)
macula框架由于采用了spring-security作为安全框架,所以直接采用了spring-security提供的CsrfFilter,可以通过macula.enableCsrf=true开启。
开启CSRF后,macula框架默认会处理AJAX请求的CSRF Token,放入头中;
如果不是AJAX提交,则需要自己在表单中加入下面的代码:
<#if _csrf?exists> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> </#if>
跨站脚本攻击防护(XSS)
TODO
越权访问防护
需要进行防止越权访问的Controller方法改造:
在需要进行校验的Controller方法上添加@PassKey注解,@PassKey可以支持@PathVariable和@RequestParam。
@PathVariable例子:
@RequestMapping(value ="/deal/edit/{dealId}", method = RequestMethod.GET)
@OpenApi
@PassKey("dealId")
public Long editDealId(@PathVariable("dealId") LongdealId) {
…
}
@RequestParam例子:
@RequestMapping(value ="/deal/edit", method = RequestMethod.GET)
@OpenApi
@PassKey({"dealId"})
public Long editDealId(@RequestParam("dealId") LongdealId) {
…
}
如果需要对多个参数值进行校验,可以传入参数名数组,例如:
@PassKey({"dealId", "storeId"})
产生访问链接的方法改造
Controller从Service获取domain list后,需要调用PassKeyHelper. generatePassKeyMap方法。方法详情如下:
/**
*根据传入参数生成passKey map
*@param list-从数据库获取的domain list
*@param params-需要校验的参数的参数名
*@param userName-用户名
*@return Map<String, String>-<passKeyId(由params拼接,例如需要校验的参数名为id和name,id值为1,name值为abc,
则passKeyId就为1abc), passkey)
*/
public static Map<String, String> generatePassKeyMap(List<?extendsObject> list, String[] params, String userName)
throws MaculaException
例如:
Map<String, String> map = PassKeyHelper.generatePassKeyMap(dataSourceManagerService.getAllDataSources(),
new String[]{"id", “code”},
SecurityUtils.getUserDetails().getName());
(亦可不传入userName,不传入则交由generatePassKeyMap方法调用SecurityUtils.getUserDetails().getName()获取)
ftl改造:
然后在freemarker遍历list的时候,用相应的字段值拼接出passKeyId(例如需要校验的参数名为id和name,id值为1,name值为abc,则passKeyId就为1abc),然后在passKeyMap中取出相应的passKey拼接在URL的后面。
例如:
@PathVariable例子:
/deal/edit/123?passKey=255A626422674E0765F51E9C8826B1A3
@RequestParam例子:
/deal/edit?dealId=123&passKey=255A626422674E0765F51E9C8826B1A3
密钥配置
可以在macula.properties:指定加密串,如不指定,将使用macula指定的默认值,例如:
pass.key.secret=djij*7dLjdKs20Kds
错误提示
当有人对参数进行篡改,如果是Ajax请求将会返回以下错误:
{"success":false,"httpStatus":null,"errorCode":"passkey",
"errorMessage":"passkey","exceptionCode":"passkey.invalid",
"exceptionMessage":"不允许平行越权访问","exceptionStack":"…}
非Ajax请求抛出异常,由error.ftl处理,默认将会得到以下页面:
例子
- 产生domain list的controller方法(这里为了展示多个参数的使用方法,用了id和code两个参数):
@RequestMapping(value ="/datasource/list", method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("datasourceList",dataSourceManagerService.getAllDataSources());
Map<String, String>passKeyMap= PassKeyHelper.generatePassKeyMap(dataSourceManagerService.getAllDataSources(),
newString[] {"id","code"});
model.addAttribute("passKeyMap",passKeyMap);
return super.getRelativePath("/datasource/list");
}
- 模板页面ftl
<div id="list-${code}" style="width: 100%;">
<table class="treeTable gridlist">
<thead>
<th>数据源编码</th>
<th>数据源名称</th>
<th>数据源类型</th>
<th>访问地址</th>
</thead>
<tbody>
<#list datasourceList as ds>
<#assign passKeyId=ds.id+ds.code />
<tr>
<td>${ds.code}</td>
<td>${ds.name}</td>
<td>${ds.dataSourceType}</td>
<td><a href="${base}/admin/macula-base/datasource/edit?id=${ds.id}&code=${ds.code}&passKey=${passKeyMap[passKeyId]}">点击访问</a>
</tr>
</#list>
</tbody>
</table>
</div>
- 需要防止越权访问的controller方法:
@RequestMapping(value ="/datasource/edit", method = RequestMethod.GET)
@PassKey({"id","code"})
public String edit(@RequestParam("id") Long id, @RequestParam("code") String code, Model model) {
model.addAttribute("id", id);
return super.getRelativePath("/datasource/edit");
}