中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring Security中粒度超細的權限控制怎么實現

發布時間:2021-12-07 13:36:21 來源:億速云 閱讀:153 作者:iii 欄目:大數據

這篇文章主要講解了“Spring Security中粒度超細的權限控制怎么實現”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Spring Security中粒度超細的權限控制怎么實現”吧!

1.準備工作

首先創建一個 Spring Boot 項目,由于我們這里涉及到數據庫操作,所以除了 Spring Security 依賴之外,還需要加入數據庫驅動以及 MyBatis 依賴。

由于沒有 acl 相關的 starter,所以需要我們手動添加 acl 依賴,另外 acl 還依賴于 ehcache 緩存,所以還需要加上緩存依賴。

最終的 pom.xml 文件如下:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-security</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-acl</artifactid>
    <version>5.3.4.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.mybatis.spring.boot</groupid>
    <artifactid>mybatis-spring-boot-starter</artifactid>
    <version>2.1.3</version>
</dependency>
<dependency>
    <groupid>mysql</groupid>
    <artifactid>mysql-connector-java</artifactid>
</dependency>
<dependency>
    <groupid>net.sf.ehcache</groupid>
    <artifactid>ehcache</artifactid>
    <version>2.10.4</version>
</dependency>
<dependency>
    <groupid>com.alibaba</groupid>
    <artifactid>druid-spring-boot-starter</artifactid>
    <version>1.1.23</version>
</dependency>
<dependency>
    <groupid>org.springframework</groupid>
    <artifactid>spring-context-support</artifactid>
</dependency>

項目創建成功之后,我們在 acl 的 jar 包中可以找到數據庫腳本文件:

Spring Security中粒度超細的權限控制怎么實現

根據自己的數據庫選擇合適的腳本執行,執行后一共創建了四張表,如下:

Spring Security中粒度超細的權限控制怎么實現

表的含義我就不做過多解釋了,不清楚的小伙伴可以參考上篇文章:Spring Security 中如何細化權限粒度?

最后,再在項目的 application.properties 文件中配置數據庫信息,如下:

spring.datasource.url=jdbc:mysql:///acls?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

至此,準備工作就算完成了。接下來我們來看配置。

2.ACL 配置

這塊配置代碼量比較大,我先把代碼擺上來,我們再逐個分析:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class AclConfig {

    @Autowired
    DataSource dataSource;

    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMIN"));
    }

    @Bean
    public PermissionGrantingStrategy permissionGrantingStrategy() {
        return new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger());
    }

    @Bean
    public AclCache aclCache() {
        return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
    }

    @Bean
    public EhCacheFactoryBean aclEhCacheFactoryBean() {
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
        ehCacheFactoryBean.setCacheName("aclCache");
        return ehCacheFactoryBean;
    }

    @Bean
    public EhCacheManagerFactoryBean aclCacheManager() {
        return new EhCacheManagerFactoryBean();
    }

    @Bean
    public LookupStrategy lookupStrategy() {
        return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger()
        );
    }

    @Bean
    public AclService aclService() {
        return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
    }

    @Bean
    PermissionEvaluator permissionEvaluator() {
        AclPermissionEvaluator permissionEvaluator = new AclPermissionEvaluator(aclService());
        return permissionEvaluator;
    }
}
  1. @EnableGlobalMethodSecurity 注解的配置表示開啟項目中 @PreAuthorize、@PostAuthorize 以及 @Secured 注解的使用,一會我們要通過這些注解配置權限。

  2. 由于引入了數據庫的一整套東西,并且配置了數據庫連接信息,所以這里可以注入 DataSource 實例以備后續使用。

  3. AclAuthorizationStrategy 實例用來判斷當前的認證主體是否有修改 Acl 的權限,準確來說是三種權限:修改 Acl 的 owner;修改 Acl 的審計信息以及修改 ACE 本身。這個接口只有一個實現類就是 AclAuthorizationStrategyImpl,我們在創建實例時,可以傳入三個參數,分別對應了這三種權限,也可以傳入一個參數,表示這一個角色可以干三件事。

  4. PermissionGrantingStrategy 接口提供了一個 isGranted 方法,這個方法就是最終真正進行權限比對的方法,該接口只有一個實現類 DefaultPermissionGrantingStrategy,直接 new 就行了。

  5. 在 ACL 體系中,由于權限比對總是要查詢數據庫,造成了性能問題,因此引入了 Ehcache 做緩存。AclCache 共有兩個實現類:SpringCacheBasedAclCache 和 EhCacheBasedAclCache。我們前面已經引入了 ehcache 實例,所以這里配置 EhCacheBasedAclCache 實例即可。

  6. LookupStrategy 可以通過 ObjectIdentity 解析出對應的 Acl。LookupStrategy 只有一個實現類就是 BasicLookupStrategy,直接 new 即可。

  7. AclService 這個我們在上文已經介紹過了,這里不再贅述。

  8. PermissionEvaluator 是為表達式 hasPermission 提供支持的。由于本案例后面使用類似于 @PreAuthorize("hasPermission(#noticeMessage, 'WRITE')") 這樣的注解進行權限控制,因此之類需要配置一個 PermissionEvaluator 實例。

至此,這里的配置類就和大家介紹完了。

3.情節設定

假設我現在有一個通知消息類 NoticeMessage,如下:

public class NoticeMessage {
    private Integer id;
    private String content;

    @Override
    public String toString() {
        return "NoticeMessage{" +
                "id=" + id +
                ", content='" + content + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

然后根據該類創建了數據表:

CREATE TABLE `system_message` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

那么接下來的權限控制就是針對這個 NoticeMessage 的。

創建 NoticeMessageMapper,并添加幾個測試方法:

@Mapper
public interface NoticeMessageMapper {
    List<noticemessage> findAll();

    NoticeMessage findById(Integer id);

    void save(NoticeMessage noticeMessage);

    void update(NoticeMessage noticeMessage);
}

NoticeMessageMapper.xml 內容如下:

<mapper namespace="org.javaboy.acls.mapper.NoticeMessageMapper">


    <select id="findAll" resulttype="org.javaboy.acls.model.NoticeMessage">
        select * from system_message;
    </select>

    <select id="findById" resulttype="org.javaboy.acls.model.NoticeMessage">
        select * from system_message where id=#{id};
    </select>

    <insert id="save" parametertype="org.javaboy.acls.model.NoticeMessage">
        insert into system_message (id,content) values (#{id},#{content});
    </insert>

    <update id="update" parametertype="org.javaboy.acls.model.NoticeMessage">
        update system_message set content = #{content} where id=#{id};
    </update>
</mapper>

這些應該都好理解,沒啥好說的。

接下來創建 NoticeMessageService,如下:

@Service
public class NoticeMessageService {
    @Autowired
    NoticeMessageMapper noticeMessageMapper;

    @PostFilter("hasPermission(filterObject, 'READ')")
    public List<noticemessage> findAll() {
        List<noticemessage> all = noticeMessageMapper.findAll();
        return all;
    }

    @PostAuthorize("hasPermission(returnObject, 'READ')")
    public NoticeMessage findById(Integer id) {
        return noticeMessageMapper.findById(id);
    }

    @PreAuthorize("hasPermission(#noticeMessage, 'CREATE')")
    public NoticeMessage save(NoticeMessage noticeMessage) {
        noticeMessageMapper.save(noticeMessage);
        return noticeMessage;
    }
    
    @PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
    public void update(NoticeMessage noticeMessage) {
        noticeMessageMapper.update(noticeMessage);
    }

}

涉及到了兩個新注解,稍微說下:

  • @PostFilter:在執行方法后過濾返回的集合或數組(篩選出當前用戶具有 READ 權限的數據),returnObject 就表示方法的返回值。有一個和它對應的注解 @PreFilter,這個注解允許方法調用,但必須在進入方法之前對參數進行過濾。

  • @PostAuthorize:允許方法調用,但是如果表達式計算結果為false,將拋出一個安全性異常,#noticeMessage 對應了方法的參數。

  • @PreAuthorize:在方法調用之前,基于表達式的計算結果來限制對方法的訪問。

明白了注解的含義,那么上面的方法應該就不用多做解釋了吧。

配置完成,接下來我們進行測試。

4.測試

為了方便測試,我們首先準備幾條測試數據,如下:

INSERT INTO `acl_class` (`id`, `class`)
VALUES
	(1,'org.javaboy.acls.model.NoticeMessage');
INSERT INTO `acl_sid` (`id`, `principal`, `sid`)
VALUES
	(2,1,'hr'),
	(1,1,'manager'),
	(3,0,'ROLE_EDITOR');
INSERT INTO `system_message` (`id`, `content`)
VALUES
	(1,'111'),
	(2,'222'),
	(3,'333');

首先添加了 acl_class,然后添加了三個 Sid,兩個是用戶,一個是角色,最后添加了三個 NoticeMessage 實例。

目前沒有任何用戶/角色能夠訪問到 system_message 中的三條數據。例如執行如下代碼獲取不到任何數據:

@Test
@WithMockUser(roles = "EDITOR")
public void test01() {
    List<noticemessage> all = noticeMessageService.findAll();
    System.out.println("all = " + all);
}

> @WithMockUser(roles = "EDITOR") 表示使用 EDITOR 角色訪問。松哥這里是為了方便。小伙伴們也可以自己給 Spring Security 配置用戶,設置相關接口,然后 Controller 中添加接口進行測試,我這里就不那么麻煩了。

現在我們對其進行配置。

首先我想設置讓 hr 這個用戶可以讀取 system_message 表中 id 為 1 的記錄,方式如下:

@Autowired
NoticeMessageService noticeMessageService;
@Autowired
JdbcMutableAclService jdbcMutableAclService;
@Test
@WithMockUser(username = "javaboy")
@Transactional
@Rollback(value = false)
public void test02() {
    ObjectIdentity objectIdentity = new ObjectIdentityImpl(NoticeMessage.class, 1);
    Permission p = BasePermission.READ;
    MutableAcl acl = jdbcMutableAclService.createAcl(objectIdentity);
    acl.insertAce(acl.getEntries().size(), p, new PrincipalSid("hr"), true);
    jdbcMutableAclService.updateAcl(acl);
}

我們設置了 mock user 是 javaboy,也就是這個 acl 創建好之后,它的 owner 是 javaboy,但是我們前面預設數據中 Sid 沒有 javaboy,所以會自動向 acl_sid 表中添加一條記錄,值為 javaboy。

在這個過程中,會分別向 acl_entry、acl_object_identity 以及 acl_sid 三張表中添加記錄,因此需要添加事務,同時因為我們是在單元測試中執行,為了確保能夠看到數據庫中數據的變化,所以需要添加 @Rollback(value = false) 注解讓事務不要自動回滾。

在方法內部,首先分別創建 ObjectIdentity 和 Permission 對象,然后創建一個 acl 對象出來,這個過程中會將 javaboy 添加到 acl_sid 表中。

接下來調用 acl_insertAce 方法,將 ace 存入 acl 中,最后調用 updateAcl 方法去更新 acl 對象即可。

配置完成后,執行該方法,執行完成后,數據庫中就會有相應的記錄了。

接下來,使用 hr 這個用戶就可以讀取到 id 為 1 的記錄了。如下:

@Test
@WithMockUser(username = "hr")
public void test03() {
    List<noticemessage> all = noticeMessageService.findAll();
    assertNotNull(all);
    assertEquals(1, all.size());
    assertEquals(1, all.get(0).getId());
    NoticeMessage byId = noticeMessageService.findById(1);
    assertNotNull(byId);
    assertEquals(1, byId.getId());
}

松哥這里用了兩個方法來和大家演示。首先我們調用了 findAll,這個方法會查詢出所有的數據,然后返回結果會被自動過濾,只剩下 hr 用戶具有讀取權限的數據,即 id 為 1 的數據;另一個調用的就是 findById 方法,傳入參數為 1,這個好理解。

如果此時想利用 hr 這個用戶修改對象,則是不可以的。我們可以繼續使用上面的代碼,讓 hr 這個用戶可以修改 id 為 1 的記錄,如下:

@Test
@WithMockUser(username = "javaboy")
@Transactional
@Rollback(value = false)
public void test02() {
    ObjectIdentity objectIdentity = new ObjectIdentityImpl(NoticeMessage.class, 1);
    Permission p = BasePermission.WRITE;
    MutableAcl acl = (MutableAcl) jdbcMutableAclService.readAclById(objectIdentity);
    acl.insertAce(acl.getEntries().size(), p, new PrincipalSid("hr"), true);
    jdbcMutableAclService.updateAcl(acl);
}

注意這里權限改為 WRITE 權限。由于 acl 中已經存在這個 ObjectIdentity 了,所以這里通過 readAclById 方法直接讀取已有的 acl 即可。方法執行完畢后,我們再進行 hr 用戶寫權限的測試:

@Test
@WithMockUser(username = "hr")
public void test04() {
    NoticeMessage msg = noticeMessageService.findById(1);
    assertNotNull(msg);
    assertEquals(1, msg.getId());
    msg.setContent("javaboy-1111");
    noticeMessageService.update(msg);
    msg = noticeMessageService.findById(1);
    assertNotNull(msg);
    assertEquals("javaboy-1111", msg.getContent());
}

此時,hr 就可以使用 WRITE 權限去修改對象了。

假設我現在想讓 manager 這個用戶去創建一個 id 為 99 的 NoticeMessage,默認情況下,manager 是沒有這個權限的。我們現在可以給他賦權:

@Test
@WithMockUser(username = "javaboy")
@Transactional
@Rollback(value = false)
public void test02() {
    ObjectIdentity objectIdentity = new ObjectIdentityImpl(NoticeMessage.class, 99);
    Permission p = BasePermission.CREATE;
    MutableAcl acl = jdbcMutableAclService.createAcl(objectIdentity);
    acl.insertAce(acl.getEntries().size(), p, new PrincipalSid("manager"), true);
    jdbcMutableAclService.updateAcl(acl);
}

注意,這里的權限是 CREATE。

接下來使用 manager 用戶就可以添加數據了:

@Test
@WithMockUser(username = "manager")
public void test05() {
    NoticeMessage noticeMessage = new NoticeMessage();
    noticeMessage.setId(99);
    noticeMessage.setContent("999");
    noticeMessageService.save(noticeMessage);
}

此時就可以添加成功了。

感謝各位的閱讀,以上就是“Spring Security中粒度超細的權限控制怎么實現”的內容了,經過本文的學習后,相信大家對Spring Security中粒度超細的權限控制怎么實現這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

青神县| 景泰县| 岫岩| 财经| 新干县| 连平县| 绥化市| 化德县| 深州市| 东乡族自治县| 三原县| 巫山县| 甘南县| 宁陕县| 古田县| 交城县| 鹤壁市| 三河市| 杭州市| 普定县| 资讯| 北宁市| 蛟河市| 洪江市| 开阳县| 千阳县| 瓮安县| 拜城县| 周至县| 建始县| 淅川县| 松阳县| 珲春市| 闽侯县| 温宿县| 长葛市| 石景山区| 延长县| 漳平市| 元朗区| 郓城县|