国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring Security 實(shí)現(xiàn)用戶授權(quán)

xfee / 709人閱讀

摘要:實(shí)現(xiàn)用戶認(rèn)證本次,我們通過的授權(quán)機(jī)制,實(shí)現(xiàn)用戶授權(quán)。啟用注解默認(rèn)的是不進(jìn)行授權(quán)注解攔截的,添加注解以啟用注解的全局方法攔截。角色該角色對(duì)應(yīng)菜單示例用戶授權(quán)代碼體現(xiàn)授權(quán)思路遍歷當(dāng)前用戶的菜單,根據(jù)菜單中對(duì)應(yīng)的角色名進(jìn)行授權(quán)。

引言

上一次,使用Spring SecurityAngular實(shí)現(xiàn)了用戶認(rèn)證。Spring Security and Angular 實(shí)現(xiàn)用戶認(rèn)證

本次,我們通過Spring Security的授權(quán)機(jī)制,實(shí)現(xiàn)用戶授權(quán)。

實(shí)現(xiàn)十分簡單,大家認(rèn)真聽,都能聽得懂。

實(shí)現(xiàn) 權(quán)限設(shè)計(jì)

前臺(tái)實(shí)現(xiàn)了菜單的權(quán)限控制,但后臺(tái)接口還沒進(jìn)行保護(hù),只要用戶登錄成功,什么接口都可以調(diào)用。

我們希望實(shí)現(xiàn):用戶有什么菜單的權(quán)限,只能訪問后臺(tái)對(duì)應(yīng)該菜單的接口。

比如,用戶有計(jì)算機(jī)組管理的菜單,就可以訪問計(jì)算機(jī)組相關(guān)的增刪改查接口,但是其他的接口都不允許訪問。

Spring Security的設(shè)計(jì)

依據(jù)Spring Security的設(shè)計(jì),用戶對(duì)應(yīng)角色,角色對(duì)應(yīng)后臺(tái)接口。這是沒什么問題的。

示例

某接口添加@Secured注解,內(nèi)部添加權(quán)限表達(dá)式。

@GetMapping
@Secured("ROLE_ADMIN")
public List getAll() {
    return hostService.getAll();
}

然后再為用戶創(chuàng)建Spring Security中的角色。

這里我們?yōu)橛脩籼砑?b>ROLE_ADMIN的角色授權(quán),與getAll方法上的@Secured("ROLE_ADMIN")注解中的參數(shù)一致,表示該用戶有權(quán)限訪問該方法,這就是授權(quán)。

private UserDetails createUser(User user) {
    logger.debug("初始化授權(quán)列表");
    List authorities = new ArrayList<>();

    logger.debug("角色授權(quán)");
    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

    logger.debug("構(gòu)建用戶");
    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
不足

作為一款優(yōu)秀的安全框架而言,Spring Security這樣設(shè)計(jì)是沒有任何問題的,我們只需要簡簡單單的幾行代碼就能實(shí)現(xiàn)接口的授權(quán)管理。

但是卻不符合我們的要求。

我們要求,在我們的系統(tǒng)中,用戶對(duì)應(yīng)多角色。

但是我們的角色是要求可以進(jìn)行動(dòng)態(tài)配置的,今天有一個(gè)系統(tǒng)管理員的角色,明天可能又加一個(gè)教師的角色。

在用戶授權(quán)這方面,是可以實(shí)現(xiàn)動(dòng)態(tài)配置的,因?yàn)橛脩舻臋?quán)限列表是一個(gè)List,我可以從數(shù)據(jù)庫查當(dāng)前用戶的角色,然后add進(jìn)去。

private UserDetails createUser(User user) {
    logger.debug("初始化授權(quán)列表");
    List authorities = new ArrayList<>();

    logger.debug("角色授權(quán)");
    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

    logger.debug("構(gòu)建用戶");
    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}

但是在接口級(jí)別,就無法實(shí)現(xiàn)動(dòng)態(tài)配置了。大家想想,注解里,要求的參數(shù)必須是常量,就是我們想動(dòng)態(tài)配置,也實(shí)現(xiàn)不了啊?

@GetMapping
@Secured("ROLE_ADMIN")
public List getAll() {
    return hostService.getAll();
}

所以,我們總結(jié),因?yàn)樽⒔馀渲玫南拗疲栽?b>Spring Security中角色是靜態(tài)的。

重新設(shè)計(jì)

我們的角色是動(dòng)態(tài)的,而Spring Security中的角色是靜態(tài)的,所以不能將我們的角色直接映射到Spring Security中的角色,要映射也得拿一個(gè)我們系統(tǒng)中靜態(tài)的對(duì)象與之對(duì)應(yīng)。

角色是動(dòng)態(tài)的,這個(gè)不行了。但是我們的菜單是靜態(tài)的啊。

功能模塊是我們開發(fā)的,菜單就這么固定的幾個(gè),用戶管理、角色管理、系統(tǒng)設(shè)置啥的,在我們開發(fā)期間就已經(jīng)固定下來了,我們是不是可以使用菜單結(jié)合Spring Security進(jìn)行授權(quán)呢?

認(rèn)真看這張圖,看懂了這張圖,你應(yīng)該就明白了我的設(shè)計(jì)思想。

角色是動(dòng)態(tài)的,我不用它授權(quán),我使用靜態(tài)的菜單進(jìn)行授權(quán)。

靜態(tài)的菜單對(duì)應(yīng)Spring Security中靜態(tài)的角色,角色再對(duì)應(yīng)后臺(tái)接口,如此設(shè)計(jì),就實(shí)現(xiàn)了我們的設(shè)想:用戶擁有哪個(gè)菜單的權(quán)限,就只擁有被該菜單調(diào)用的相應(yīng)接口權(quán)限。

編碼

設(shè)計(jì)好了,一起來寫代碼吧。

授權(quán)注解選擇

Spring Security中有多種授權(quán)注解,個(gè)人經(jīng)過對(duì)比之后選擇@Secured注解,因?yàn)槲矣X得這個(gè)注解配置項(xiàng)更容易被人理解。

public @interface Secured {
    /**
     * Returns the list of security configuration attributes (e.g. ROLE_USER, ROLE_ADMIN).
     *
     * @return String[] The secure method attributes
     */
    public String[]value();
}

直接寫一個(gè)角色的字符串?dāng)?shù)組傳進(jìn)去即可。

@Secured("ROLE_ADMIN")                      // 需要擁有`ROLE_ADMIN`角色才可訪問
@Secured({"ROLE_ADMIN", "ROLE_TEACHER"})    // 用戶擁有`ROLE_ADMIN`、`ROLE_TEACHER`二者之一即可訪問

注意:這里的字符串一定是以ROLE_開頭,Spring Security才把它當(dāng)成角色的配置,否則無效。

啟用@Secured注解

默認(rèn)的Spring Security是不進(jìn)行授權(quán)注解攔截的,添加注解@EnableGlobalMethodSecurity以啟用@Secured注解的全局方法攔截。

@EnableGlobalMethodSecurity(securedEnabled = true)         // 啟用全局方法安全,采用@Secured方式
菜單角色映射

在菜單中新建一個(gè)字段securityRoleName來聲明我們的系統(tǒng)菜單對(duì)應(yīng)著哪個(gè)Spring Security角色。

// 該菜單在Spring Security環(huán)境下的角色名稱
@Column(nullable = false)
private String securityRoleName;

建一個(gè)類,用于存放所有Spring Security角色的配置信息,供全局調(diào)用。

這里不能用枚舉,@Secured注解中要求必須是String數(shù)組,如果是枚舉,需要通過YunzhiSecurityRoleEnum.ROLE_MAIN.name()格式獲取字符串信息,但很遺憾,注解中要求必須是常量。

還記得上次自定義HTTP狀態(tài)碼的時(shí)候,吃了枚舉類無法擴(kuò)展的虧,以后再也不用枚舉了。就算用枚舉,也會(huì)設(shè)計(jì)一個(gè)接口,枚舉實(shí)現(xiàn)該接口,不用枚舉聲明方法的參數(shù)類型,而使用接口聲明,方便擴(kuò)展。

package club.yunzhi.huasoft.security;

/**
 * @author zhangxishuo on 2019-03-02
 * Yunzhi Security 角色
 * 該角色對(duì)應(yīng)菜單
 */
public class YunzhiSecurityRole {

    public static final String ROLE_MAIN = "ROLE_MAIN";

    public static final String ROLE_HOST = "ROLE_HOST";

    public static final String ROLE_GROUP = "ROLE_GROUP";

    public static final String ROLE_USER = "ROLE_USER";

    public static final String ROLE_ROLE = "ROLE_ROLE";

    public static final String ROLE_SETTING = "ROLE_SETTING";

}

示例

@GetMapping
@Secured({YunzhiSecurityRole.ROLE_HOST, YunzhiSecurityRole.ROLE_GROUP})
public List getAll() {
    return hostService.getAll();
}
用戶授權(quán)

代碼體現(xiàn)授權(quán)思路:遍歷當(dāng)前用戶的菜單,根據(jù)菜單中對(duì)應(yīng)的Security角色名進(jìn)行授權(quán)。

private UserDetails createUser(User user) {
    logger.debug("獲取用戶的所有授權(quán)菜單");
    Set menus = webAppMenuService.getAllAuthMenuByUser(user);

    logger.debug("初始化授權(quán)列表");
    List authorities = new ArrayList<>();

    logger.debug("遍歷授權(quán)菜單,進(jìn)行角色授權(quán)");
    for (WebAppMenu menu : menus) {
        authorities.add(new SimpleGrantedAuthority(menu.getSecurityRoleName()));
    }

    logger.debug("構(gòu)建用戶");
    return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}

注:這里遇到了Hibernate惰性加載引起的錯(cuò)誤,啟用事務(wù)防止Hibernate關(guān)閉Session,深層原理目前還在研究。

單元測試

單元測試很簡單,供寫相同功能的人參考。

@Test
public void authTest() throws Exception {
    logger.debug("獲取基礎(chǔ)菜單");
    WebAppMenu hostMenu = webAppMenuRepository.findByRoute("/host");
    WebAppMenu groupMenu = webAppMenuRepository.findByRoute("/group");
    WebAppMenu settingMenu = webAppMenuRepository.findByRoute("/setting");

    logger.debug("構(gòu)造角色");
    List roleList = new ArrayList<>();

    Role roleHost = new Role();
    roleHost.setWebAppMenuList(Collections.singletonList(hostMenu));
    roleList.add(roleHost);

    Role roleGroup = new Role();
    roleGroup.setWebAppMenuList(Collections.singletonList(groupMenu));
    roleList.add(roleGroup);

    Role roleSetting = new Role();
    roleSetting.setWebAppMenuList(Collections.singletonList(settingMenu));
    roleList.add(roleSetting);

    logger.debug("保存角色");
    roleRepository.saveAll(roleList);

    logger.debug("構(gòu)造用戶");
    User user = userService.getOneUnSavedUser();

    logger.debug("獲取用戶名和密碼");
    String username = user.getUsername();
    String password = user.getPassword();

    logger.debug("保存用戶");
    userRepository.save(user);

    logger.debug("用戶登錄");
    String token = this.loginWithUsernameAndPassword(username, password);

    logger.debug("無授權(quán)用戶訪問host,斷言403");
    this.mockMvc.perform(MockMvcRequestBuilders.get(HOST_URL)
            .header(TOKEN_KEY, token))
            .andExpect(status().isForbidden());

    logger.debug("用戶授權(quán)Host菜單");
    user.getRoleList().clear();
    user.getRoleList().add(roleHost);
    userRepository.save(user);

    logger.debug("重新登錄, 重新授權(quán)");
    token = this.loginWithUsernameAndPassword(username, password);

    logger.debug("授權(quán)Host用戶訪問,斷言200");
    this.mockMvc.perform(MockMvcRequestBuilders.get(HOST_URL)
            .header(TOKEN_KEY, token))
            .andExpect(status().isOk());

    logger.debug("用戶授權(quán)Group菜單");
    user.getRoleList().clear();
    user.getRoleList().add(roleGroup);
    userRepository.save(user);

    logger.debug("重新登錄, 重新授權(quán)");
    token = this.loginWithUsernameAndPassword(username, password);

    logger.debug("授權(quán)Group用戶訪問,斷言200");
    this.mockMvc.perform(MockMvcRequestBuilders.get(HOST_URL)
            .header(TOKEN_KEY, token))
            .andExpect(status().isOk());

    logger.debug("用戶授權(quán)Setting菜單");
    user.getRoleList().clear();
    user.getRoleList().add(roleSetting);
    userRepository.save(user);

    logger.debug("重新登錄, 重新授權(quán)");
    token = this.loginWithUsernameAndPassword(username, password);

    logger.debug("授權(quán)Setting用戶訪問,斷言403");
    this.mockMvc.perform(MockMvcRequestBuilders.get(HOST_URL)
            .header(TOKEN_KEY, token))
            .andExpect(status().isForbidden());
}

private String loginWithUsernameAndPassword(String username, String password) throws Exception {
    logger.debug("用戶登錄");
    byte[] encodedBytes = Base64.encodeBase64((username + ":" + password).getBytes());
    MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders.get(LOGIN_URL)
            .header("Authorization", "Basic " + new String(encodedBytes)))
            .andExpect(status().isOk())
            .andReturn();

    logger.debug("從返回體中獲取token");
    String json = mvcResult.getResponse().getContentAsString();
    JSONObject jsonObject = JSON.parseObject(json);
    return jsonObject.getString("token");
}
總結(jié)
感謝開源社區(qū),感謝Spring Security

五行代碼(不算注釋),一個(gè)注解。就解決了一直以來困擾我們的權(quán)限問題。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/11476.html

相關(guān)文章

  • Spring Security

    摘要:框架具有輕便,開源的優(yōu)點(diǎn),所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實(shí)現(xiàn)身份驗(yàn)證往期譯見系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評(píng)論0 收藏0
  • 源碼-Spring Security Oauth2

    摘要:給定一個(gè)作為方法參數(shù)傳遞的域?qū)ο髮?shí)例,確保類要綁定合適的權(quán)限。對(duì)或或檢查與驗(yàn)證。檢查是否在客戶端的權(quán)限范圍內(nèi)。匹配默認(rèn)的前綴字符串是的,如果匹配到則授權(quán),如授權(quán)范圍。實(shí)現(xiàn)類輪詢所有配置的每個(gè)配置,并且如果全部是肯定才能授予訪問權(quán)限。 03.01-源碼-Spring Security Oauth2 @(技術(shù)-架構(gòu))[源碼, 權(quán)限, Security, Oauth2] Oauth2 是一個(gè)...

    AWang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

xfee

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<