CKEditor与CKFinder学习--安全的使用CKFinder

0 前言

上一篇博客中说了ckeditor与ckfinder整合之后进行图文混排的时候通过浏览服务器按钮可以浏览服务器上的所有资源,文件,图片,该用户上传的,非该用户上传的,不仅可以查看,还可以删除、重命名等。所以不是很安全。

CKEditor与CKFinder学习--安全的使用CKFinder_第1张图片

CKEditor与CKFinder学习--安全的使用CKFinder_第2张图片

上一篇博客介绍了一种方法就是直接屏蔽掉浏览服务器的按钮,这样用户就只能上传,通过上传后再引用。不过尽管把按钮屏蔽了,但是链接的路径依然存在。
通过访问
http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html
用户依然可以访问到文件浏览的页面

CKEditor与CKFinder学习--安全的使用CKFinder_第3张图片
所以本文的第一个就是介绍如何不让用户访问http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html页面。
不让用户访问上面的页面,可以有很多种方法,可以参考之前的Shiro进行拦截,然而本文今天介绍使用SpringMVC的拦截器对其进行拦截。

1 通过拦截器阻止直接访问

1.1 定义拦截器

实现HandlerInterceptor接口

package com.gwc.cktest.intercepter;

import java.util.Enumeration;

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

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class CkfinderInterceepter implements HandlerInterceptor {
    private static final Log logger = LogFactory.getLog(CkfinderInterceepter.class);

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        logger.info("==========走到了 afterCompletion() 方法");
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView model)
            throws Exception {
        logger.info("==========走到了 postHandle() 方法");
    }

    // 返回值,表示我们是否需要将当前的请求拦截下来
    // 如果返回false,请求将被终止,如果为true,请求将被放行
    // Object arg2 表示的是被拦截的请求的目标对象
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
        logger.info("==========走到了 preHandle() 方法"+"==========");
        logger.info("==========进行拦截==========");
        return false;
    }

}

1.2 配置拦截器和拦截规则

在SpringMVC的配置文件中进行拦截器的配置和拦截规则的设定

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截规则 -->
            <mvc:mapping path="/**/ckfinder.html" />
            <!-- 拦截器 -->
            <bean class="com.gwc.cktest.intercepter.CkfinderInterceepter"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

因为我们需要拦截的是
http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html
所以我们的拦截规则设置为/**/ckfinder.html

1.3 效果

没有配置拦截器

配置拦截器之后

这种方式有点类似CSDN写博客了,只允许上传图片或者引用其他在线图片,不能浏览
CKEditor与CKFinder学习--安全的使用CKFinder_第4张图片

2 细粒度的权限控制

细粒度的权限控制中,我们将不会全部拦截对http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html的请求,而是进行有条件的拦截,条件就是ckfinder权限中的角色session对应的key为CKFinder_UserRole,我们在拦截器中需要查看session中是否有有CKFinder_UserRole再决定放行与否。在用户登录的时候往session中写CKFinder_UserRole。
具体流程图如下。

先来配置资源访问的访问控制(ACL),这部分在ckfinder.xml中进行配置。

2.1 配置ckfinder.xml

首先阅读一下ckfinder的开发者文档
http://docs.cksource.com/CKFinder_2.x/Developers_Guide/Java/Configuration/Access_Control
它介绍了访问控制,主要是通过配置accessControls来控制用户的权限的

<!-- The session variable name that CKFinder must use to retrieve the "role" of the current user. The "role", can be used in the "accessControls" settings (bellow). -->
    <userRoleSessionVar>CKFinder_UserRole</userRoleSessionVar>
    <accessControls>
        <accessControl>
            <role>*</role>
            <resourceType>Images</resourceType>
            <folder>/Logos</folder>
            <folderView>true</folderView>
            <folderCreate>true</folderCreate>
            <folderRename>true</folderRename>
            <folderDelete>true</folderDelete>
            <fileView>true</fileView>
            <fileUpload>false</fileUpload>
            <fileRename>false</fileRename>
            <fileDelete>false</fileDelete>
        </accessControl>
    </accessControls>

所有的权限都在accessControls标签中,具体的权限被配置在一个个accessControl中
其中第一个role标签代表的是角色,与其称作角色不如称为key为CKFinder_UserRole的session,在userRoleSessionVar可以配置。
你可以配置自己喜欢的session使其key为CKFinder_UserRole的value为你喜欢的任何字符串,不过最好能见名知意,*表示是所有的用户都可以访问。
其他标签也很好理解resourceType表示资源类型,以folder开头的是对文件夹的操作权限,以file开头的是对文的操作权限。

2.1.1 权限配置

现在我们配置两种权限,一个是admin,权限最大,什么权限都有,另一个是user权限,他只能查看根目录下的Images资源,而且对文件夹有只读操作和创建操作,对文件具有查看和上传操作。

    <userRoleSessionVar>CKFinder_UserRole</userRoleSessionVar>
    <accessControls>
        <accessControl>
            <role>admin</role>
            <resourceType>*</resourceType>
            <folder>/</folder>
            <folderView>true</folderView>
            <folderCreate>true</folderCreate>
            <folderRename>true</folderRename>
            <folderDelete>true</folderDelete>
            <fileView>true</fileView>
            <fileUpload>true</fileUpload>
            <fileRename>true</fileRename>
            <fileDelete>true</fileDelete>
        </accessControl>
        <accessControl>
            <role>user</role>
            <resourceType>Images</resourceType>
            <folder>/</folder>
            <folderView>true</folderView>
            <folderCreate>true</folderCreate>
            <folderRename>false</folderRename>
            <folderDelete>false</folderDelete>
            <fileView>true</fileView>
            <fileUpload>true</fileUpload>
            <fileRename>false</fileRename>
            <fileDelete>false</fileDelete>
        </accessControl>
    </accessControls>

2.2 拦截器实现

将上面的拦截器进行修改,只改preHandle方法,如果session中有CKFinder_UserRole则放行,否则拦截请求。

    // 返回值,表示我们是否需要将当前的请求拦截下来
    // 如果返回false,请求将被终止,如果为true,请求将被放行
    // Object arg2 表示的是被拦截的请求的目标对象
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
        logger.info("==========走到了 preHandle() 方法"+"==========");
        HttpSession session = request.getSession();
        String user = (String) session.getAttribute("CKFinder_UserRole");
        if (StringUtils.isNotEmpty(user)) {
            logger.info("==========session中的CKFinder_UserRole:"+user+"==========");
            return true;
        }
        logger.info("==========session中没有CKFinder_UserRole进行拦截==========");
        return false;
    }

2.3 登录时写session

@RequestMapping(value = "/admin.html", method = RequestMethod.POST)
    @ResponseBody
    public String ckAdmin(HttpServletRequest request, String userId) {
        logger.info("==========" + userId + "登陆了" + "==========");
        HttpSession session=request.getSession();
        // 这里的逻辑很简单,如果是admin就将session中CKFinder_UserRole设为admin,其他的设置为user
        if (userId.equals("admin")) {
            session.setAttribute("CKFinder_UserRole", "admin");
        } else {
            session.setAttribute("CKFinder_UserRole", "user");
        }
        logger.info("==========" + "session 设置成功" + "==========");
        return "session 设置成功";
    }

退出时清空session

@RequestMapping(value = "/logout.html", method = RequestMethod.POST)
    @ResponseBody
    public String ckLogout(HttpServletRequest request) {
        HttpSession session = request.getSession();
        Enumeration<?> e = session.getAttributeNames();
        while (e.hasMoreElements()) {
            String sessionName = (String) e.nextElement();
            logger.info("==========存在的session有:" + sessionName + ":"+session.getAttribute(sessionName)+"==========");
            session.removeAttribute(sessionName);
        }
        logger.info("==========" + "session 移除成功" + "==========");
        return "session 移除成功";
    }

2.4 演示

2.4.1 没有session

当用户没有登录,也就是说session中还什么都没有的时候

用户通过浏览服务器什么也干不了

http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html也是无法访问的。

这是日志信息

CKEditor与CKFinder学习--安全的使用CKFinder_第5张图片

2.4.2 session中CKFinder_UserRole为user

这个时候用户只能浏览,上传,不能删除照片,重命名照片

CKEditor与CKFinder学习--安全的使用CKFinder_第6张图片

当然,通过http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html直接访问,用户只能看到Images文件夹,对文件夹也只能读和创建操作

2.4.3 session中CKFinder_UserRole为admin

这个时候用户可以进行所有的操作

当然,通过http://127.0.0.1:8080/ckdemo/assets/ckfinder/ckfinder.html直接访问可以看到其他的文件夹,且具有删除操作

ckfinder的安全使用就写到这里。

示例地址

https://github.com/peer44/ckeditor

参考文献

http://docs.cksource.com/CKFinder_2.x/Developers_Guide/Java/Configuration/Access_Control

你可能感兴趣的:(ckfinder)