构建带鉴权系统的文档管理平台Writerside

原文链接

欢迎大家对于本站的访问 - AsterCasc

前言

构建一个鉴权系统是简单的事情。但是对于文档管理平台,如何将各种形式文档转换成HTML的形式,以及对于不同转换元素样式的调整,甚至支持UML/Mermaid图、文档版本管理等等,这些的工作量如果单人完成可以说是巨大的。所以我们我们这里需要一个工具,将简单的Markdown文档变为一个可控的文档管理平台。在前文制作在线Markdown文档转Html以及Pdf工具Marked.js渲染下md内图片点击放大解决方案等文章都有使用各种工具做过类似的操作,可以在Markdown转换看到效果,在文档较少或者没有成体系地对外/内预览需求,那么确实可以如此处理。但是仅仅这些是无法作为文档管理平台的具体解决方案的

鉴权系统

对于鉴权系统其实没什么可谈的,无非就时用户表,权限表,角色表啥等,然后根据后台配置在网关侧或者服务侧做相应接口过滤,我们这里就不聊业务的处理部分了,总之我们认为目前已经存在了一个系统,提供登录、鉴权相关功能,并且登录页和Writerside处于同一个域名下。当然如果已经有了登录页,希望做单点登录也不是什么难事,做个公共的认证中心页面即可,或者直接改动之前登录页面作为公共认证中心

Writerside

这里我们仍然使用Jetbrains全家桶中的Writerside,没有证书的小伙伴参考免费获得IDEA证书,关于如何使用这个软件工作,官方文档解释地很清楚了,这里没有必要重复,我们这里主要聊以下文档中没有提及的或者一笔带过但是比较重要的东西

自定义页脚

我们注意到使用Writerside生成文档管理平台后,包括logofavicons、实例标题都可以自定义,但是目前版本2024.1.0页面展示上几乎算唯一不可定义的东西就是页脚的Powered by JetBrains Writerside。我们这里需要稍微使用一些奇技淫巧,文档中提到可以使用<include-in-head>来增加一些可以自定义的头标签,我们可以在头标签中复写样式,来达到隐藏页脚的目的

最后构建buildprofiles.xml如下:

<?xml version="1.0" encoding="UTF-8"?>  
<buildprofiles xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/build-profiles.xsd"  
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
  
  
    <build-profile instance="ad">  
        <variables>    
		    <!--else-->
		    <include-in-head>patch.html</include-in-head>
	        <!--else-->
        </variables>   
    <footer>
	    <copyright>2020-2024 astercasc.com  
            | 互联网ICP备案:浙ICP备2022023127号  
	    </copyright>  
    </footer>  
  
</buildprofiles>

patch.html

<style>  
    .footer__powered {  
        display: none;  
    }  
  
    .footer__wrapper {  
        padding-right: 0;  
        display: flex;  
        justify-content: center;  
    }  
  
    .wh-header__link {  
        display: flex;  
        align-items:center;  
    }  
  
    .wh-header__product-logo {  
        height: 36px;  
    }  
</style>

这里我稍微调整了以下logo部分的样式,小伙伴也可以根据自己的喜好覆盖样式。稍微提一嘴,目前版本的<footer>内的标签极其难用,基本上没有好用的,除了样式有问题就是本身就有问题,《点名表扬》<icp>,说是为了中国大陆开发的,然后发现中国大陆就一个上海

添加请求头

如果我们需要对文档进行鉴权就要了解当前用户,就需要根据token来区分用户并且在后端进行鉴权返回结果。这个token将在用户登录的时候被存入localStorage,所以我们需要取出并附在所有静态资源的请求中,那么我们应该如何在Writerside项目中实现这个需求呢,还是利用<include-in-head>,在patch.html中添加:

<script>    
    if ('serviceWorker' in navigator) {  
        navigator.serviceWorker.register('/service-worker.js')  
            .then(function (registration) {  
                let curToken = localStorage.getItem('User-Token')  
                if (registration.active) {  
                    if(!curToken) {  
                        curToken = ''  
                    }  
                    registration.active.postMessage({  
                        type: 'SET_USER_TOKEN',  
                        userToken: curToken  
                    });  
                }  
            }).catch(function (error) {  
            console.log('Service Worker registration failed:', error);  
        });  
    }  
    
</script>

我们这里使用serviceWorker完成该功能,将service-worker.js注册完成后将token发送过去,完成静态资源请求头的携带,service-worker.js如下:

let userToken = '';

self.addEventListener('install', function(event) {
    self.skipWaiting();
});

self.addEventListener('activate', function(event) {
    event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', function(event) {
    event.respondWith(
        (async function() {
            const originalHeaders = new Headers(event.request.headers);
            if(userToken) {
                originalHeaders.set('User-Token', userToken);
            }
            const modifiedRequest = new Request(event.request, {
                headers: originalHeaders,
            });
            try {
                const response = await fetch(modifiedRequest);
                return response;
            } catch (error) {
                console.error('Fetch failed:', error);
                throw error;
            }
        })()
    );
});

self.addEventListener('message', function(event) {
    if (event.data && event.data.type === 'SET_USER_TOKEN') {
        userToken = event.data.userToken || '';
    }
});

导航栏触发鉴权操作

我们会发现在进行静态资源的请求后,如果用户没有登录或者没有权限从而无法请求到资源时,如果只是重定向那么使用nginx即可解决。但有些小伙伴可能需要一些更人性化的提示或者个性化的操作,这里的实现方式也是和上面一样,在<include-in-head><script>添加:


	function yourMatchRule() {
		//...
	}
	
    function sendHttpRequestWithXHR(init = false) {  
        if (init && !yourMatchRule()) {  
            return;  
        }  
        var xhr = new XMLHttpRequest();  
        xhr.open('GET', 'https://domin.com/exampleauthcheck', true);  
        let curToken = localStorage.getItem('User-Token')  
        if (curToken) {  
            xhr.setRequestHeader('User-Token', localStorage.getItem('User-Token'));  
        }  
        xhr.onreadystatechange = function () {  
            if (xhr.readyState === 4) {  
                if (xhr.status === 401) {  
                    window.location.pathname = '/login';  
                    //console.log("error")  
                } else {  
                    //console.log("success")  
                }  
            }  
        };  
        xhr.send();  
    }  

    window.addEventListener('load', () => {  
        let tocList = document.getElementsByClassName("toc")  
        if (tocList && tocList.length > 0) {  
            let toc = tocList[0];  
            toc.addEventListener('click', function (event) {  
                if (event.target.tagName === 'A') {  
                    if (yourMatchRule()) {  
                        sendHttpRequestWithXHR()  
                    }  
                }  
            });  
        }  
    });  


我们这里为右侧导航栏添加点击事件,当请求页面满足某种个性化配置时候,触发接口鉴权操作,如果结果状态为401则返回登录页

反向代理配置

这里以Nginx举例:

    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;
        server_name  doc.astercasc.com;

		#some base setting

        location ~ (your_match_regular) {
            auth_request /auth;
            error_page 401 = @error401;
            root /examplePath/doc.astercasc.com;
            try_files $uri =404;
        }
        
        location = /auth {
            internal;
            set $auth_request_url $scheme://$host$request_uri;
            proxy_pass http://localhost:9527/exampleauthcheck?url=$auth_request_url;
			#some base setting
        }


        location  /documents {
            root /examplePath/doc.astercasc.com/;
            try_files $uri $uri/ /starter.html;
        }

        location = /documents/ {
            return 301 /documents/starter.html;
        }

        location  /service-worker.js {
            root /examplePath/doc.astercasc.com/work/;
        }


        location  / {
            root /examplePath/doc.astercasc.com/frame;
            try_files $uri $uri/ /index.html =404;
        }

        location @error401 {
            return 301 /login;
        }

		#some base setting

    }

这里假设doc.astercasc.com下有三个文件夹framedocumentswork分别放置基础项目,文档管理平台、脚本。首先定义一个需要鉴权的匹配正则,如果你这里全部需要鉴权,则直接使用Writerside发布的所在文件夹即可。鉴权成功直接允许对于该文件的访问。当用户没有权限时,则通过异常状态401直接重定向到基础项目的登录页,当不满足需要鉴权的资源请求时候时,定位到文档管理平台相应路径

原文链接

欢迎大家对于本站的访问 - AsterCasc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值