Thymeleaf自定義標(biāo)簽實現(xiàn)自定義數(shù)據(jù)權(quán)限

背景

后臺系統(tǒng)要支持第三方合作伙伴畏浆,且每個第三方開通的模塊都不相同鉴扫。以往的權(quán)限無法滿足需求身害。這里重新實現(xiàn)一套數(shù)據(jù)權(quán)限味悄。每個門診維護(hù)一套配置。

條件顯示處理器

Thymeleaf給我們提供了條件顯示處理器(AbstractConditionalVisibilityAttrProcessor)塌鸯。我們只需要繼承這個處理器即可侍瑟。

@Component
public class ModuleEnableAttrProcessor extends AbstractConditionalVisibilityAttrProcessor {

    //優(yōu)先級比sec:authorize(300)靠前。保證模塊先執(zhí)行丙猬。
    public static final int ATTR_PRECEDENCE = AuthorizeAttrProcessor.ATTR_PRECEDENCE - 1;
    public static final String ATTR_NAME = "enable";

    @Autowired
    @Lazy
    private ModuleEnableExpressionService moduleEnableExpressionService;

    public ModuleEnableAttrProcessor() {
        super(ATTR_NAME);
    }

    //控制顯示與否的方法涨颜。
    @Override
    protected boolean isVisible(Arguments arguments, Element element, String attributeName) {
        //自定義屬性值。
        final String attributeValue = element.getAttributeValue(attributeName);
        Integer clinicId = ClinicIdContextHolder.getClinicId();
        return moduleEnableExpressionService.parseExpressionByClinicId(clinicId, attributeValue);
    }

    @Override
    public int getPrecedence() {
        return ATTR_PRECEDENCE;
    }
}

這里由于模塊權(quán)限優(yōu)先級比較高茧球,故設(shè)置precedence=AuthorizeAttrProcessor.ATTR_PRECEDENCE - 1庭瑰,只需要實現(xiàn)isVisible方法,返回true:顯示袜腥,false:不顯示钉汗。

復(fù)雜表達(dá)式的支持

由于門診首頁狀態(tài)欄比較特殊羹令,會有多個模塊共同決定是否顯示鲤屡,或者一些模塊啟用,一些模塊禁用等復(fù)雜情況福侈,這里借鑒security的表達(dá)式(不知道有沒有其它的更好的方法)酒来。

    public Boolean parseExpressionByClinicId(Integer clinicId, String expression){
        if(StringUtils.isBlank(expression)) return false;
        SpelExpressionParser template = new SpelExpressionParser();
        SpelExpression spelExpression = (SpelExpression)template.parseExpression(expression);
        return _handleSpelNode(clinicId, spelExpression.getAST());
    }

這里取巧,通過SpelExpressionParser來解析復(fù)雜表達(dá)式肪凛,比如:BOOKING AND !EXAM表示有BOOKING配置堰汉,且沒有EXAM配置。解析出來SpelNode再逐步的判斷AND,OR,!語法伟墙,最后得出結(jié)果翘鸭。代碼如下:

private Boolean _handleSpelNode(Integer clinicId, SpelNode ast){
        Class<?> clazz = ast.getClass();
        if(clazz == OpAnd.class){
            return _handleAnd(clinicId, (OpAnd)ast);//處理and
        } else if(clazz == OpOr.class){
            return _handleOr(clinicId, (OpOr)ast);//處理or
        } else if(clazz == OperatorNot.class){
            return _handleNot(clinicId, (OperatorNot)ast);//處理!
        } else if(clazz == PropertyOrFieldReference.class){
            return _handleReference(clinicId, (PropertyOrFieldReference)ast);
        } else {
            return false;
        }
    }
    
    private boolean _handleReference(Integer clinicId, PropertyOrFieldReference reference){
        String name = reference.getName().toUpperCase();
        return clinicModuleService.isModuleEnabled(clinicId, ClinicModule.byName(name));
    }

這里的_handleAnd戳葵,_handleOr就乓,_handleNot采用遞歸的方法調(diào)用_handleSpelNode找到PropertyOrFieldReference里面的值并處理。判斷是否設(shè)置了模塊權(quán)限拱烁,通過redis將配置緩存起來生蚁,減少數(shù)據(jù)庫的壓力。

初始化處理器

將條件顯示處理器增加到thymeleaf處理器中


@Component
public class ModuleEnableDialect extends AbstractDialect {

    public static final String DEFAULT_PREFIX = "module";

    @Autowired
    private ModuleEnableAttrProcessor moduleEnableAttrProcessor;

    @Override
    public String getPrefix() {
        return DEFAULT_PREFIX;
    }

    @Override
    public Set<IProcessor> getProcessors() {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(moduleEnableAttrProcessor);
        return processors;
    }
}

Thymeleaf模板應(yīng)用

只需要在標(biāo)簽中加入 DEFAULT_PREFIX:ATTR_NAME屬性戏自,屬性的值即為條件表達(dá)式邦投。

    <li data-type="warehouse" module:enable="MEDICINE or EXAM or CONSUMABLE" sec:authorize="hasPermission('warehouse', 'manage')"><a th:href="@{/warehouse/consume}"><i class="icon-inventory"></i>庫存</a></li>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市擅笔,隨后出現(xiàn)的幾起案子志衣,更是在濱河造成了極大的恐慌,老刑警劉巖猛们,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件念脯,死亡現(xiàn)場離奇詭異,居然都是意外死亡阅懦,警方通過查閱死者的電腦和手機和二,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耳胎,“玉大人惯吕,你說我怎么就攤上這事∨挛纾” “怎么了废登?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長郁惜。 經(jīng)常有香客問我堡距,道長甲锡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任羽戒,我火速辦了婚禮缤沦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘易稠。我一直安慰自己缸废,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布驶社。 她就那樣靜靜地躺著企量,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亡电。 梳的紋絲不亂的頭發(fā)上届巩,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音份乒,去河邊找鬼恕汇。 笑死,一個胖子當(dāng)著我的面吹牛冒嫡,可吹牛的內(nèi)容都是我干的拇勃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼孝凌,長吁一口氣:“原來是場噩夢啊……” “哼方咆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蟀架,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤瓣赂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后片拍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煌集,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年捌省,在試婚紗的時候發(fā)現(xiàn)自己被綠了苫纤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纲缓,死狀恐怖卷拘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祝高,我是刑警寧澤栗弟,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站工闺,受9級特大地震影響乍赫,放射性物質(zhì)發(fā)生泄漏瓣蛀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一雷厂、第九天 我趴在偏房一處隱蔽的房頂上張望惋增。 院中可真熱鬧,春花似錦罗侯、人聲如沸器腋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诊县,卻和暖如春讲弄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背依痊。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工避除, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胸嘁。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓瓶摆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親性宏。 傳聞我的和親對象是個殘疾皇子群井,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容