国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

gateway-統(tǒng)一權(quán)限-認(rèn)證

這篇具有很好參考價(jià)值的文章主要介紹了gateway-統(tǒng)一權(quán)限-認(rèn)證。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

int## 基礎(chǔ)名詞概念
**權(quán)限:**屬于系統(tǒng)的安全范疇,權(quán)限管理實(shí)現(xiàn)對(duì)用戶訪問(wèn)系統(tǒng)的控制,按照安全規(guī)則或者安全控制策略用戶可以訪問(wèn)而且只能訪問(wèn)自己被授權(quán)的資源,主要包括用戶身份認(rèn)證和請(qǐng)求鑒權(quán)兩部分,簡(jiǎn)稱認(rèn)證鑒權(quán)
認(rèn)證判斷一個(gè)用戶是否為合法用戶的處理過(guò)程,最常用的簡(jiǎn)單身份認(rèn)證是系統(tǒng)通過(guò)核對(duì)用戶輸入的用戶名和口令,看其是否與系統(tǒng)中存儲(chǔ)的該用戶的用戶名和口令一致,來(lái)判斷用戶身份是否正確
gateway-統(tǒng)一權(quán)限-認(rèn)證

鑒權(quán):即訪問(wèn)控制,控制誰(shuí)能訪問(wèn)那些資源;進(jìn)行身份認(rèn)證后需要分配權(quán)限可訪問(wèn)的系統(tǒng)資源,對(duì)于某些資源沒(méi)有權(quán)限是無(wú)法訪問(wèn)的,如下圖所示

gateway-統(tǒng)一權(quán)限-認(rèn)證
權(quán)限控制:用戶是某個(gè)角色、或擁有某個(gè)資源時(shí),才可訪問(wèn)系統(tǒng)資源我們稱之為權(quán)限控制,權(quán)限控制分為下列2類型:
基于角色
RBAC基于角色的訪問(wèn)控制是以角色為中心進(jìn)行訪問(wèn)控制,比如:主體的角色為總經(jīng)理可以查詢企業(yè)運(yùn)營(yíng)報(bào)表,查詢員工薪資信息等,訪問(wèn)控制流程如下:
gateway-統(tǒng)一權(quán)限-認(rèn)證
基于資源
RBAC基于資源的訪問(wèn)控制,是以資源中心進(jìn)行訪問(wèn)控制,企業(yè)中常用的權(quán)限管理方法,實(shí)現(xiàn)思路是:將系統(tǒng)操作的每個(gè)URL配置在資源表中,將資源對(duì)應(yīng)到角色,將角色分配給用戶,用戶訪問(wèn)系統(tǒng)功能通過(guò)Filter進(jìn)行過(guò)濾,過(guò)濾器獲取到用戶的url,只要訪問(wèn)的url是用戶分配角色中的URL是用戶分配角色的url則進(jìn)行訪問(wèn),其具體流程如下:
gateway-統(tǒng)一權(quán)限-認(rèn)證
匿名資源:無(wú)需認(rèn)證鑒權(quán)就可以訪問(wèn)的資源
公共資源:只需登錄既可以訪問(wèn)的資源

多平臺(tái)權(quán)限控制

xxxx作為一個(gè)SaaS平臺(tái),商家提供運(yùn)營(yíng)主體信息后,運(yùn)營(yíng)平臺(tái)會(huì)為商家開通系統(tǒng),各個(gè)商家平臺(tái)都需要在運(yùn)營(yíng)平臺(tái)的管理下去工作:
1、運(yùn)營(yíng)平臺(tái)可以管理所有商家平臺(tái)的企業(yè)信息
2、運(yùn)營(yíng)平臺(tái)可以管理所有商家平臺(tái)的資源信息
3、運(yùn)營(yíng)平臺(tái)可以管理所有商家平臺(tái)的角色信息
4、運(yùn)營(yíng)平臺(tái)可以管理商家平臺(tái)的用戶信息

第二章 基礎(chǔ)信息簡(jiǎn)介

在開始做權(quán)限開發(fā)之前我們需要看下權(quán)限設(shè)計(jì)的數(shù)據(jù)庫(kù)結(jié)構(gòu):
gateway-統(tǒng)一權(quán)限-認(rèn)證
通過(guò)上圖,我們可以得到如下的信息:
一個(gè)企業(yè)可以有多個(gè)用戶
一個(gè)用戶可以有多個(gè)角色
一個(gè)角色可以有多個(gè)資源
這個(gè)是經(jīng)典的權(quán)限設(shè)計(jì),也就是:企業(yè),用戶,角色,資源通過(guò)它們可以來(lái)完成整個(gè)權(quán)限的控制。

企業(yè)信息

商家想申請(qǐng)入駐平臺(tái),首先在申請(qǐng)頁(yè)面【也可以后端錄入】進(jìn)行信息填寫,填寫完成【運(yùn)營(yíng)平臺(tái)】對(duì)商家資質(zhì)進(jìn)行審核,審核通過(guò)后商家即可入職使用,如圖所示:

gateway-統(tǒng)一權(quán)限-認(rèn)證
數(shù)據(jù)庫(kù)結(jié)構(gòu)設(shè)計(jì)

CREATE TABLE `tab_enterprise` (
  `id` bigint(18) NOT NULL,
  `enterprise_id` bigint(18) NOT NULL COMMENT '商戶ID【系統(tǒng)內(nèi)部識(shí)別使用】',
  `enterprise_name` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '企業(yè)名稱',
  `enterprise_no` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '工商號(hào)',
  `province` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(省)',
  `area` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(區(qū))',
  `city` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(市)',
  `address` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '詳細(xì)地址',
  `status` varchar(8) COLLATE utf8_bin NOT NULL COMMENT '狀態(tài)(試用:trial,停用:stop,正式:official)',
  `proposer_Id` bigint(18) DEFAULT NULL COMMENT '申請(qǐng)人Id',
  `enable_flag` varchar(18) CHARACTER SET utf8 NOT NULL COMMENT '是否有效',
  `created_time` datetime NOT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime NOT NULL COMMENT '創(chuàng)建時(shí)間',
  `expire_time` datetime NOT NULL COMMENT '到期時(shí)間 (試用下是默認(rèn)七天后到期,狀態(tài)改成停用)',
  `web_site` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '商戶門店web站點(diǎn)',
  `sharding_id` bigint(18) NOT NULL COMMENT '分庫(kù)id',
  `app_web_site` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '商戶h5web站點(diǎn)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='企業(yè)賬號(hào)管理';

實(shí)現(xiàn)細(xì)節(jié)
對(duì)于這個(gè)功能的CRUD這里就不做贅述,這里主要思考2個(gè)問(wèn)題:
為什么我們要為商家綁定域名?
通過(guò)一個(gè)圖來(lái)分析下整個(gè)的工作流程
gateway-統(tǒng)一權(quán)限-認(rèn)證
員工在瀏覽器中發(fā)起ppsk.shop.eehp.cn的訪問(wèn)請(qǐng)求
阿里云域名解析會(huì)把ppsk.shop.eehp網(wǎng)址解析到阿里云ECS服務(wù)器101.101.108.2
阿里云ECS服務(wù)器【101.101.108.2】宿主機(jī)會(huì)把信息轉(zhuǎn)發(fā)到docker-nginx服務(wù)器
docker-nginx服務(wù)器配置的serverName【*.shop.eehp.cn】轉(zhuǎn)發(fā)到gateway服務(wù)
gateway服務(wù)根據(jù)ppsk.shop.eehp.cn兌換企業(yè)號(hào)100001
根據(jù)企業(yè)號(hào)100001訪問(wèn)目標(biāo)的商家A
域名和企業(yè)號(hào)如何建立關(guān)聯(lián)
在security模塊的initEnterpriseWeb方法,這里主要有四個(gè)方法:
init:初始化企業(yè)站點(diǎn)信息到redis,此方法上有==@PostConstruct==注解,表示項(xiàng)目啟動(dòng)時(shí)即加載信息
addWebSiteforRedis:添加緩存中的站點(diǎn),當(dāng)我們【新增】企業(yè)主體信息時(shí)調(diào)用此方法
deleteWebSiteForRedis:移除緩存中的站點(diǎn),當(dāng)我們【刪除,僅用】企業(yè)主體信息時(shí)調(diào)用此方法
updateWebSiteforRedis:更新緩存中的站點(diǎn),當(dāng)我們修改禁用企業(yè)主體信息時(shí)調(diào)用此方法

/**
 * @ClassName initEnterpriseWebSIteInfo.java
 * @Description 初始化企業(yè)站點(diǎn)信息到redis
 */
@Component
public class InitEnterpriseSite {

    @Autowired
    IEnterpriseService enterpriseService;

    @Autowired
    RedissonClient redissonClient;

    /**
     *獲得兩時(shí)間的秒間隔
     */
    public Long secondInterval(Date date1, Date date2) {
        long secondInterval = (date2.getTime() - date1.getTime()) / 1000;
        return secondInterval;
    }

    /***
     * @description 初始化企業(yè)站點(diǎn)信息到redis
     */
    @PostConstruct
    public void init(){
        QueryWrapper<Enterprise> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Enterprise::getEnableFlag, SuperConstant.YES)
            .and(wrapper->wrapper
                .eq(Enterprise::getStatus,SuperConstant.TRIAL)
                .or()
                .eq(Enterprise::getStatus,SuperConstant.OFFICIAL));
        List<Enterprise> list = enterpriseService.list(queryWrapper);
        List<EnterpriseVo> enterpriseVos = BeanConv.toBeanList(list, EnterpriseVo.class);
        for (EnterpriseVo enterpriseVo : enterpriseVos) {
            String webSiteKey = SecurityCacheConstant.WEBSITE+enterpriseVo.getWebSite();
            RBucket<EnterpriseVo> webSiteBucket = redissonClient.getBucket(webSiteKey);
            String appWebSiteKey = SecurityCacheConstant.APP_WEBSITE+enterpriseVo.getAppWebSite();
            RBucket<EnterpriseVo> appWebSiteBucket = redissonClient.getBucket(appWebSiteKey);
            Long secondInterval = this.secondInterval(new Date(), enterpriseVo.getExpireTime());
            if (secondInterval.longValue()>0){
                webSiteBucket.set(enterpriseVo,secondInterval, TimeUnit.SECONDS);
                appWebSiteBucket.set(enterpriseVo,secondInterval, TimeUnit.SECONDS);
            }
        }
    }

    /***
     * @description 添加緩存中的站點(diǎn)
     * @param enterpriseVo 企業(yè)號(hào)
     * @return:
     */
    public void addWebSiteforRedis(EnterpriseVo enterpriseVo){
        String webSiteKey = SecurityCacheConstant.WEBSITE+enterpriseVo.getWebSite();
        RBucket<EnterpriseVo> webSiteBucket = redissonClient.getBucket(webSiteKey);
        String appWebSiteKey = SecurityCacheConstant.APP_WEBSITE+enterpriseVo.getAppWebSite();
        RBucket<EnterpriseVo> appWebSiteBucket = redissonClient.getBucket(appWebSiteKey);
        Long secondInterval = this.secondInterval(new Date(), enterpriseVo.getExpireTime());
        if (secondInterval.longValue()>0){
            webSiteBucket.trySet(enterpriseVo,secondInterval, TimeUnit.SECONDS);
            appWebSiteBucket.trySet(enterpriseVo,secondInterval, TimeUnit.SECONDS);
        }
    }

    /***
     * @description 移除緩存中的站點(diǎn)
     * @param enterpriseVo 企業(yè)號(hào)
     * @return:
     */
    public void deleteWebSiteforRedis( EnterpriseVo enterpriseVo){
        String webSiteKey = SecurityCacheConstant.WEBSITE+enterpriseVo.getWebSite();
        RBucket<EnterpriseVo> webSiteBucket = redissonClient.getBucket(webSiteKey);
        String appWebSiteKey = SecurityCacheConstant.APP_WEBSITE+enterpriseVo.getAppWebSite();
        RBucket<EnterpriseVo> appWebSiteBucket = redissonClient.getBucket(appWebSiteKey);
        webSiteBucket.delete();
        appWebSiteBucket.delete();
    }


    /***
     * @description 更新緩存中的站點(diǎn)
     * @param enterpriseVo 企業(yè)號(hào)
     * @return:
     */
    public void updataWebSiteforRedis(EnterpriseVo enterpriseVo){
        String webSiteKey = SecurityCacheConstant.WEBSITE+enterpriseVo.getWebSite();
        RBucket<EnterpriseVo> webSiteBucket = redissonClient.getBucket(webSiteKey);
        String appWebSiteKey = SecurityCacheConstant.APP_WEBSITE+enterpriseVo.getAppWebSite();
        RBucket<EnterpriseVo> appWebSiteBucket = redissonClient.getBucket(appWebSiteKey);
        Long secondInterval = this.secondInterval(new Date(), enterpriseVo.getExpireTime());
        if (secondInterval.longValue()>0){
            webSiteBucket.set(enterpriseVo,secondInterval, TimeUnit.SECONDS);
            appWebSiteBucket.set(enterpriseVo,secondInterval, TimeUnit.SECONDS);
        }
    }

考慮到企業(yè)表【Enterprise】做CRUD的時(shí)候會(huì)影響緩存的更新,需要在EnterpriseFaceImpl中做同步的處理

@Override
public EnterpriseVo createEnterprise(EnterpriseVo eterperiseVo) {
    Enterprise enterpriseResult = EnterpriseService.createEnterprise(eterperiseVo);
    //同步緩存
    if (!EmptyUtil.isNullOrEmpty(enterpriseResult)){
        initEnterpriseWebSiteInfo.addWebSiteforRedis(eterperiseVo.getWebSite(),eterperiseVo);
    }
    return BeanConv.toBean(enterpriseResult,EnterpriseVo.class);
}

@Override
public Boolean updateEnterprise(EnterpriseVo enterpriseVo) {
    Boolean flag = EnterpriseService.updateEnterprise(enterpriseVo);
    //同步緩存
    if (flag){
        if (enterpriseVo.getEnableFlag().equals(SuperConstant.YES)){
            initEnterpriseWebSiteInfo.updataWebSiteforRedis(enterpriseVo.getWebSite(),enterpriseVo);
        }else {
            initEnterpriseWebSiteInfo.deleteWebSiteforRedis(enterpriseVo.getWebSite(),enterpriseVo);
        }
    }
    return flag;
}

@Override
public Boolean deleteEnterprise(String[] checkedIds) {
    //同步緩存
    for (String checkedId : checkedIds) {
        Enterprise enterprise = EnterpriseService.getById(checkedId);
        EnterpriseVo enterpriseVo = BeanConv.toBean(enterprise, EnterpriseVo.class);
        initEnterpriseWebSiteInfo.deleteWebSiteforRedis(enterprise.getWebSite(),enterpriseVo);
    }
    Boolean flag =  EnterpriseService.deleteEnterprise(checkedIds);
    return flag;
}
CREATE TABLE `tab_resource` (
  `id` bigint(18) NOT NULL COMMENT '主鍵',
  `parent_id` bigint(18) DEFAULT NULL COMMENT '父Id',
  `resource_name` varchar(36) DEFAULT NULL COMMENT '資源名稱',
  `request_path` varchar(200) DEFAULT NULL COMMENT '資源路徑',
  `icon` varchar(20) DEFAULT NULL COMMENT '圖標(biāo)',
  `is_leaf` varchar(18) DEFAULT NULL COMMENT '是否葉子節(jié)點(diǎn)',
  `resource_type` varchar(36) DEFAULT NULL COMMENT '資源類型',
  `sort_no` int(11) DEFAULT NULL COMMENT '排序',
  `description` varchar(200) DEFAULT NULL COMMENT '描述',
  `system_code` varchar(36) DEFAULT NULL COMMENT '系統(tǒng)歸屬',
  `is_system_root` varchar(18) DEFAULT NULL COMMENT '是否根節(jié)點(diǎn)',
  `enable_flag` varchar(18) DEFAULT NULL COMMENT '是否有效',
  `created_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sharding_id` bigint(18) DEFAULT NULL,
  `label` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='資源表';

為了解決信息系統(tǒng)中的訪問(wèn)控制管理的問(wèn)題,適當(dāng)簡(jiǎn)化授權(quán)工作量,提高權(quán)限管理效率,需要建立基于角色的多系統(tǒng)授權(quán)管理模型,其業(yè)務(wù)管理模式如下:
運(yùn)營(yíng)平臺(tái)系統(tǒng)管理員負(fù)責(zé)角色的權(quán)限及用戶權(quán)限及用戶分配。
運(yùn)營(yíng)平臺(tái)系統(tǒng)管理員負(fù)責(zé)角色的權(quán)限匹配,同時(shí)賦予商家管理員對(duì)角色分配用戶的權(quán)限,定義標(biāo)準(zhǔn)角色,實(shí)現(xiàn)權(quán)限管理的部分下放
在此模式下,系統(tǒng)管理員不再兼任單位管理員工作,需要實(shí)現(xiàn)權(quán)限的多級(jí)下放,其架構(gòu)設(shè)計(jì)如圖所示
gateway-統(tǒng)一權(quán)限-認(rèn)證

數(shù)據(jù)庫(kù)結(jié)構(gòu):
角色表:

CREATE TABLE `tab_role` (
  `id` bigint(18) NOT NULL COMMENT '主鍵',
  `role_name` varchar(36) DEFAULT NULL COMMENT '角色名稱',
  `label` varchar(36) DEFAULT NULL COMMENT '角色標(biāo)識(shí)',
  `description` varchar(200) DEFAULT NULL COMMENT '角色描述',
  `sort_no` int(36) DEFAULT NULL COMMENT '排序',
  `enable_flag` varchar(18) DEFAULT NULL COMMENT '是否有效',
  `created_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sharding_id` bigint(18) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色表';

角色資源表:

CREATE TABLE `tab_role_resource` (
  `id` bigint(18) NOT NULL,
  `enable_flag` varchar(18) DEFAULT NULL,
  `role_id` bigint(18) DEFAULT NULL,
  `resource_id` bigint(18) DEFAULT NULL,
  `created_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sharding_id` bigint(18) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色資源表';

用戶信息

xxx系統(tǒng)中用戶分為:運(yùn)營(yíng)商員工、商家平臺(tái)員工,其信息的維護(hù)規(guī)則如下:
運(yùn)營(yíng)平臺(tái)系統(tǒng)管理員:可以定義,管理所有的用戶,并且從角色中選擇權(quán)限
運(yùn)營(yíng)平臺(tái)系統(tǒng)管理員:負(fù)責(zé)定義角色,同時(shí)賦予商家管理員對(duì)角色,商家管理員分配用戶的權(quán)限(定義標(biāo)準(zhǔn)角色,實(shí)現(xiàn)權(quán)限管理的部分下放)。
多個(gè)運(yùn)營(yíng)商之間的員工信息是相互隔絕的

數(shù)據(jù)庫(kù)結(jié)構(gòu)
用戶表:

CREATE TABLE `tab_user` (
  `id` bigint(18) NOT NULL COMMENT '主鍵',
  `store_id` bigint(32) DEFAULT NULL COMMENT '門店Id',
  `enterprise_id` bigint(18) NOT NULL COMMENT '商戶號(hào)',
  `username` varchar(36) DEFAULT NULL COMMENT '登錄名稱',
  `real_name` varchar(36) DEFAULT NULL COMMENT '真實(shí)姓名',
  `password` varchar(150) DEFAULT NULL COMMENT '密碼',
  `sex` varchar(11) DEFAULT NULL COMMENT '性別',
  `mobil` varchar(36) DEFAULT NULL COMMENT '電話',
  `email` varchar(36) DEFAULT NULL COMMENT '郵箱',
  `discount_limit` decimal(10,2) DEFAULT NULL COMMENT '折扣上線',
  `reduce_limit` decimal(10,2) DEFAULT NULL COMMENT '減免金額上線',
  `duties` varchar(36) DEFAULT NULL COMMENT '職務(wù)',
  `sort_no` int(11) DEFAULT NULL COMMENT '排序',
  `enable_flag` varchar(18) DEFAULT NULL COMMENT '是否有效',
  `created_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sharding_id` bigint(18) DEFAULT NULL COMMENT '分庫(kù)id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用戶表';

用戶角色表

CREATE TABLE `tab_user_role` (
  `id` bigint(36) NOT NULL,
  `enable_flag` varchar(18) DEFAULT NULL,
  `user_id` bigint(18) DEFAULT NULL,
  `role_id` bigint(18) DEFAULT NULL,
  `created_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `updated_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  `sharding_id` bigint(18) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用戶角色表';

統(tǒng)一權(quán)限認(rèn)證

認(rèn)證:判斷一個(gè)用戶是否為合法用戶的處理過(guò)程,最常用的簡(jiǎn)單身份認(rèn)證方式是系統(tǒng)通過(guò)核對(duì)用戶輸入的用戶名和口令,看其是否與系統(tǒng)中存儲(chǔ)的該用戶和口令一致,來(lái)判斷用戶身份是否正確,如下圖所示:
gateway-統(tǒng)一權(quán)限-認(rèn)證
集成的方式:本權(quán)限是基于spring-cloud-gateway網(wǎng)關(guān)來(lái)做權(quán)限的控制,因?yàn)間ateway是基于響應(yīng)式webflux【響應(yīng)式編程】的機(jī)制進(jìn)行處理的,所以這里的用法和原本httpservlet是有所區(qū)別的,首先我們來(lái)看一下整體的模塊依賴處理:
gateway-統(tǒng)一權(quán)限-認(rèn)證
商家發(fā)起請(qǐng)求到gateway-shop網(wǎng)關(guān)
gateway網(wǎng)關(guān)調(diào)用model-security-client,進(jìn)行認(rèn)證或鑒權(quán)過(guò)濾器
model-security-client作為服務(wù)消費(fèi)者通過(guò)model-security-interface接口進(jìn)行用戶認(rèn)證、鑒權(quán)接口調(diào)用
model-security-producer作為服務(wù)生產(chǎn)者進(jìn)行當(dāng)前用戶登錄、角色、權(quán)限的查詢
model-security-client:模塊是本權(quán)限系統(tǒng)核心,他提供了具體的認(rèn)證、鑒權(quán)的邏輯,如果一個(gè)gateway想要實(shí)現(xiàn)權(quán)限的控制只需要依賴此客戶端
gateway-統(tǒng)一權(quán)限-認(rèn)證
gateway-統(tǒng)一權(quán)限-認(rèn)證

認(rèn)證流程總述

gateway-統(tǒng)一權(quán)限-認(rèn)證
認(rèn)證總體流程如下:
用戶在登錄頁(yè)選擇登錄方式
判斷登錄方式是短信登錄、賬號(hào)密碼登錄,進(jìn)行域名校驗(yàn),兌換企業(yè)ID【enterpriseid】
通過(guò)服務(wù)鑒權(quán)轉(zhuǎn)換器ServerAuthenticationConverter構(gòu)建權(quán)限對(duì)象Authentication
Authentication對(duì)象交于認(rèn)證管理器【ReactiveAuthenticationManager】進(jìn)行認(rèn)證

服務(wù)鑒權(quán)轉(zhuǎn)換器
gateway-統(tǒng)一權(quán)限-認(rèn)證
ServerAuthenticationConverter:主要是負(fù)責(zé)表單的自動(dòng)轉(zhuǎn)換,在spring-security中的默認(rèn)的登錄頁(yè)面是long頁(yè)面,我們需要從表單中獲取用戶名和密碼或者用戶短信驗(yàn)證碼

package com.xxxx.restkeeper.converter;

import com.itheima.restkeeper.converter.LoginConverter;
import com.itheima.restkeeper.utils.EmptyUtil;
import com.itheima.restkeeper.utils.RegisterBeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @ClassName ReactiveFormLoginAuthenticationConverter.java
 * @Description 自定義表單轉(zhuǎn)換
 */
@Component
public class ReactiveServerAuthenticationConverter implements ServerAuthenticationConverter {

    //登錄方式
    private String loginTypeParameter = "loginType";

    //站點(diǎn)類型
    private String siteTypeParameter = "siteType";

    @Autowired
    RegisterBeanHandler registerBeanHandler;

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        String loginType = exchange.getRequest().getHeaders().getFirst("loginType");
        String siteType = exchange.getRequest().getHeaders().getFirst("siteType");
        if (EmptyUtil.isNullOrEmpty(loginType)){
            throw  new BadCredentialsException("客戶登陸異常");
        }
        LoginConverter loginConverter = registerBeanHandler.getBean(loginType, LoginConverter.class);
        return loginConverter.convert(exchange,loginType,siteType);
    }
}

**LoginConverter:**登錄轉(zhuǎn)換接口定義

import org.springframework.security.core.Authentication;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @ClassName LoginTypeConverterHandler.java
 * @Description 登錄類型轉(zhuǎn)換接口
 */
public interface LoginConverter {

    /***
     * @description 登錄轉(zhuǎn)換
     * @param exchange
     * @param loginType
     * @param siteType
     * @return
     */
    public Mono<Authentication> convert(ServerWebExchange exchange,
                                        String loginType,
                                        String siteType);
}

**MobilLoginConverter:**手機(jī)驗(yàn)證碼登錄轉(zhuǎn)換器

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @ClassName SystemMobilLoginConverter.java
 * @Description 手機(jī)登錄
 */
@Component("mobilLogin")
public class MobilLoginConverter implements LoginConverter {

    //手機(jī)
    private String mobileParameter = "mobile";

    //驗(yàn)證碼
    private String authCodeParameter = "authCode";

    @Autowired
    RedissonClient redissonClient;

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange,
                                        String loginType,
                                        String siteType) {
        String hostName = exchange.getRequest().getURI().getHost();
        String key = null;
        if (siteType.equals(SuperConstant.WEBSITE)){
            key = SecurityCacheConstant.WEBSITE+hostName;
        }else if (siteType.equals(SuperConstant.APP_WEBSITE)){
            key = SecurityCacheConstant.APP_WEBSITE+hostName;
        }else {
            return  Mono.error(new BadCredentialsException("站點(diǎn)類型未定義"));
        }
        //域名校驗(yàn)
        RBucket<EnterpriseVo> bucket = redissonClient.getBucket(key);
        EnterpriseVo enterpriseVo = bucket.get();
        if (EmptyUtil.isNullOrEmpty(enterpriseVo)){
            return  Mono.error(new BadCredentialsException("Invalid hostName"));
        }
        //獲得enterpriseId
        String enterpriseId = String.valueOf(enterpriseVo.getEnterpriseId());
        return exchange.getFormData().map( data -> {
            String mobile = data.getFirst(this.mobileParameter);
            String authCode = data.getFirst(this.authCodeParameter);
            if (EmptyUtil.isNullOrEmpty(mobile)||
                    EmptyUtil.isNullOrEmpty(authCode)){
                throw  new BadCredentialsException("客戶登陸異常");
            }
            String principal = mobile+":"+enterpriseId+":"+loginType+":"+siteType;
            return new UsernamePasswordAuthenticationToken(principal, authCode);
        });
    }
}

**UsernameLoginConverter:**用戶名密碼登錄

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @ClassName SysUsernameLoginConverterHandler.java
 * @Description 系統(tǒng)賬號(hào)密碼轉(zhuǎn)換
 */
@Component("usernameLogin")
public class UsernameLoginConverter implements LoginConverter {

    //賬號(hào)
    private String usernameParameter = "username";

    //密碼
    private String passwordParameter = "password";

    @Autowired
    RedissonClient redissonClient;

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange,
                                        String loginType,
                                        String siteType) {
        String hostName = exchange.getRequest().getURI().getHost();
        String key = null;
        if (siteType.equals(SuperConstant.WEBSITE)){
            key = SecurityCacheConstant.WEBSITE+hostName;
        }else if (siteType.equals(SuperConstant.APP_WEBSITE)){
            key = SecurityCacheConstant.APP_WEBSITE+hostName;
        }else {
            return  Mono.error(new BadCredentialsException("站點(diǎn)類型未定義"));
        }
        //域名校驗(yàn)
        RBucket<EnterpriseVo> bucket = redissonClient.getBucket(key);
        EnterpriseVo enterpriseVo = bucket.get();
        if (EmptyUtil.isNullOrEmpty(enterpriseVo)){
            return  Mono.error(new BadCredentialsException("Invalid hostName"));
        }
        //獲得enterpriseId
        String enterpriseId = String.valueOf(enterpriseVo.getEnterpriseId());
        return exchange.getFormData().map( data -> {
            String username = data.getFirst(this.usernameParameter);
            String password = data.getFirst(this.passwordParameter);
            if (EmptyUtil.isNullOrEmpty(username)||
                EmptyUtil.isNullOrEmpty(password)){
                throw  new BadCredentialsException("用戶登陸異常");
            }
            String principal = username+":"+enterpriseId+":"+loginType+":"+siteType;
            return new UsernamePasswordAuthenticationToken(principal, password);
        });
    }
}

用戶信息明細(xì)

gateway-統(tǒng)一權(quán)限-認(rèn)證
ReactiveUserDetailsServiceImpl:主要負(fù)責(zé)認(rèn)證過(guò)程,對(duì)于用戶信息的獲得,這里分為四種獲得方式
user賬戶登錄
user手機(jī)登錄
customer賬戶登錄
customer手機(jī)登錄
統(tǒng)一調(diào)用UserAdapterFace或者CustomerAdapterFace進(jìn)行用戶登錄消息的獲得方式

import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.HashSet;

/**
 * @ClassName ReactiveUserDetailsServiceImpl.java
 * @Description 支持flum的身份類實(shí)現(xiàn)ReactiveUserDetailsService接口
 */
@Component("reactiveUserDetailsService")
@Slf4j
public class ReactiveUserDetailsServiceImpl implements ReactiveUserDetailsService{

    //調(diào)用RPC原創(chuàng)服務(wù)
    @DubboReference(version = "${dubbo.application.version}", check = false)
    UserAdapterFace userAdapterFace;

    //調(diào)用RPC原創(chuàng)服務(wù)
    @DubboReference(version = "${dubbo.application.version}", check = false)
    CustomerAdapterFace customerAdapterFace;

    //驗(yàn)證身份
    @Override
    public Mono<UserDetails> findByUsername(String principal) {
        String[] principals = principal.split(":");
        if (principals.length!=4){
            log.warn("用戶:{}登錄信息不完整",principal);
            return Mono.empty();
        }
        String mobile =principals[0];
        String username =principals[0];
        Long enterpriseId =Long.valueOf(principals[1]);
        String loginType =principals[2];
        String siteType =principals[3];
        UserVo userVo = null;
        //user賬戶登錄
        if (loginType.equals(SuperConstant.USERNAME_LOGIN)
            &&siteType.equals(SuperConstant.WEBSITE)){
            userVo = userAdapterFace.findUserByUsernameAndEnterpriseId(username, enterpriseId);
        }
        //user手機(jī)登錄
        if (loginType.equals(SuperConstant.MOBIL_LOGIN)
            &&siteType.equals(SuperConstant.WEBSITE)){
            userVo = userAdapterFace.findUserByMobilAndEnterpriseId(mobile, enterpriseId);
        }
        //customer賬戶登錄
        if (loginType.equals(SuperConstant.USERNAME_LOGIN)
            &&siteType.equals(SuperConstant.APP_WEBSITE)){
            userVo = customerAdapterFace.findCustomerByUsernameAndEnterpriseId(username, enterpriseId);
        }
        //customer手機(jī)登錄
        if (loginType.equals(SuperConstant.MOBIL_LOGIN)
            &&siteType.equals(SuperConstant.APP_WEBSITE)){
            userVo = customerAdapterFace.findCustomerByMobilAndEnterpriseId(mobile, enterpriseId);
        }
        if (EmptyUtil.isNullOrEmpty(userVo)){
            log.warn("用戶:{}不存在",principal);
            return Mono.empty();
        }
        UserAuth userAuth = new UserAuth(
            userVo.getUsername(),
            userVo.getPassword(),
            new HashSet<>(),
            userVo.getId(),
            userVo.getShardingId(),
            userVo.getEnterpriseId(),
            userVo.getStoreId(),
            userVo.getJwtToken(),
            userVo.getRealName(),
            userVo.getSex(),
            userVo.getMobil(),
            userVo.getEmail(),
            userVo.getDiscountLimit(),
            userVo.getReduceLimit(),
            userVo.getDuties(),
            userVo.getCreatedTime(),
            userVo.getUpdatedTime()
        );
        return Mono.just(userAuth);
    }
}

認(rèn)證管理器
gateway-統(tǒng)一權(quán)限-認(rèn)證
**JwtReactiveAuthenticationManager:**查詢用戶明細(xì)信息之后,與請(qǐng)求傳遞過(guò)來(lái)的密碼或者短信驗(yàn)證碼進(jìn)行比對(duì),如果比對(duì)成功則表示登錄成功

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;

/**
 * @ClassName JwtUserDetailsRepositoryReactiveAuthenticationManager.java
 * @Description 認(rèn)證管理器
 */
@Component
public class JwtReactiveAuthenticationManager implements ReactiveAuthenticationManager {

    //密碼編輯者
    private PasswordEncoder passwordEncoder = PasswordEncoderFactories
            .createDelegatingPasswordEncoder();

    //調(diào)度程序
    private Scheduler scheduler = Schedulers.parallel();

    //用戶明細(xì)信息服務(wù)
    @Autowired
    private ReactiveUserDetailsService reactiveUserDetailsService;

    @Autowired
    RedissonClient redissonClient;

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        final String principal = authentication.getName();
        final String password = (String) authentication.getCredentials();
        String[] principals = principal.split(":");
        //mobile+":"+enterpriseId+":"+loginType+":"+siteType
        String mobile =principals[0];
        String enterpriseId =principals[1];
        String loginType =principals[2];
        String siteType =principals[3];
        Mono<UserDetails> userDetailsMono = this.reactiveUserDetailsService.findByUsername(principal);
        //密碼校驗(yàn)
        if (loginType.equals(SuperConstant.USERNAME_LOGIN)){
            return userDetailsMono.publishOn(this.scheduler)
                //密碼比較
                .filter(u -> this.passwordEncoder.matches(password, u.getPassword()))
                //失敗處理
                .switchIfEmpty(Mono.defer(()->
                    Mono.error(new BadCredentialsException("Invalid Credentials"))))
                //成功處理
                .map(u ->
                    new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()));
        }
        //短信校驗(yàn)
        if (loginType.equals(SuperConstant.MOBIL_LOGIN)){
            //redis中獲得驗(yàn)證碼
            String key = SmsCacheConstant.LOGIN_CODE+principals[0];
            RBucket<String> bucket = redissonClient.getBucket(key);
            String authCode = bucket.get();
            if (EmptyUtil.isNullOrEmpty(authCode)){
                Mono.error(new BadCredentialsException("Invalid Credentials"));
            }
            return userDetailsMono.publishOn(this.scheduler)
                //密碼比較
                .filter(u -> authCode.equals(password))
                //失敗處理
                .switchIfEmpty(Mono.defer(()->
                    Mono.error(new BadCredentialsException("Invalid Credentials"))))
                //成功處理
                .map(u ->
                    new UsernamePasswordAuthenticationToken(u, u.getPassword(), u.getAuthorities()));
        }
        throw new BadCredentialsException("Invalid Credentials");
    }

    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
        this.passwordEncoder = passwordEncoder;
    }

}

認(rèn)證成功
gateway-統(tǒng)一權(quán)限-認(rèn)證

import io.netty.util.CharsetUtil;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.*;

/**
 * @ClassName JsonServerAuthenticationSuccessHandler.java
 * @Description 登錄成功handler
 */
@Component
public class JsonServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {

    @Autowired
    JwtTokenManager jwtTokenManager;

    @DubboReference(version = "${dubbo.application.version}", check = false)
    UserAdapterFace userAdapterFace;

    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, 
                                              Authentication authentication) {
        ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
        UserAuth authUser = (UserAuth) authentication.getPrincipal();
        //構(gòu)建userVo返回對(duì)象
        UserVo userVo = UserVo.builder()
                .id(authUser.getId())
                .username(authUser.getUsername())
                .reduceLimit(authUser.getReduceLimit())
                .discountLimit(authUser.getDiscountLimit())
                .enterpriseId(authUser.getEnterpriseId())
                .storeId(authUser.getStoreId())
                .build();
        //處理角色構(gòu)建
        List<RoleVo> roleByUserId = userAdapterFace.findRoleByUserId(userVo.getId());
        Set<String> roles =  new HashSet<>();
        for (RoleVo roleVo : roleByUserId) {
            roles.add(roleVo.getLabel());
        }
        //處理資源構(gòu)建
        List<ResourceVo> resourceByUserId = userAdapterFace.findResourceByUserId(userVo.getId());
        Set<String> resources = new HashSet<>();
        for (ResourceVo resourceVo : resourceByUserId) {
            resources.add(resourceVo.getRequestPath());
        }
        //用戶指定角色、資源
        userVo.setRoles(roles);
        userVo.setResources(resources);
        //構(gòu)建JWT令牌
        Map<String,Object> claims = new HashMap<>();
        String userVoJsonString= JSONObject.toJSONString(userVo);
        claims.put("currentUser",userVoJsonString);
        String jwtToken = jwtTokenManager.issuedToken("system",
                jwtTokenManager.getJwtProperties().getTtl(),
                authUser.getId().toString(),
                claims);
        userVo.setJwtToken(jwtToken);
        //返回信息給前端
        ResponseWrap<UserVo> responseWrap = ResponseWrapBuild.build(AuthEnum.SUCCEED, userVo);
        String result = JSONObject.toJSONString(responseWrap);
        DataBuffer buffer = response.bufferFactory().wrap(result.getBytes(CharsetUtil.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}

認(rèn)證失敗
認(rèn)證失敗:只需要返回錯(cuò)誤信息即可文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-415083.html

import io.netty.util.CharsetUtil;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * @ClassName JsonServerAuthenticationFailureHandler.java
 * @Description 登錄失敗
 */
@Component
public class JsonServerAuthenticationFailureHandler implements ServerAuthenticationFailureHandler {

    @Override
    public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
        //指定應(yīng)答狀態(tài)
        ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
        response.setStatusCode(HttpStatus.OK);
        response.getHeaders().set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
        //返回信息給前段
        ResponseWrap<UserAuth> responseWrap = ResponseWrapBuild.build(AuthEnum.FAIL, null);
        String result = JSONObject.toJSONString(responseWrap);
        DataBuffer buffer = response.bufferFactory().wrap(result.getBytes(CharsetUtil.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}

到了這里,關(guān)于gateway-統(tǒng)一權(quán)限-認(rèn)證的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包