hasAuthority和hasRole区别

项目场景:

想要实现菜单的动态展示,不同角色权限看到的菜单不同

之前一直想实现root角色下,可以看到所有的level,user角色下只能看到level1。
sec:authorize=“hasAuthority(‘root’)”
sec:authorize=“hasRole(‘root’)”


问题描述

动态菜单不生效

在实现过程中发现,页面总是什么都不显示。(此时的可能只有一种,就是html中的sec:authorize="hasRole('root')"未生效),但是在页面上已经能看到,获取到的权限是库里的root

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>首页</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
    <link th:href="@{/css/qinstyle.css}" rel="stylesheet">
</head>

<body>

<!--主容器-->
<div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item"  th:href="@{/index}">首页</a>

            <!--登录注销-->
            <div class="right menu">
                <!--!isAuthenticated()如果未登录-->
                <div sec:authorize="!isAuthenticated()">
                    <a class="item" th:href="@{/toLoginPage}">
                        <i class="address card icon"></i> 登录
                    </a>
                </div>


                <!--如果已登录:用户名,注销-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        用户名:<span sec:authentication="name"> </span>
<!--                        principal.authorities获得用户的权限-->
                        角色:<span sec:authentication="principal.authorities"> </span>
                    </a>
                </div>
                <div sec:authorize="isAuthenticated()">
<!--                    如果想改成post,需要改成表单form才行-->
                    <a class="item" th:href="@{/logout}">
                        <i class="sign-out icon"></i> 注销
                    </a>
                </div>

                <!--已登录
                <a th:href="@{/usr/toUserCenter}">
                    <i class="address card icon"></i> admin
                </a>
                -->
            </div>
        </div>
    </div>

    <div class="ui segment" style="text-align: center">
        <h3>Spring Security Study by 秦疆</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">
            <!--菜单根据用户的角色动态的实现-->
            <div class="column" sec:authorize="hasRole('root')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column" sec:authorize="hasRole('root')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column" sec:authorize="hasRole('root')">
            <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>

</div>


<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/js/semantic.min.js}"></script>

</body>
</html>

原因分析:

就是因为不了解hasRole和hasAuthority的底层源码逻辑。代码中设置的hasRole(‘root’),check时候是ROLE_root,而数据库中存的是root,因此无法生效

hasRole及hasAuthority的使用区别
身份不加ROLE_前缀

身份ADMIN能通过的权限 @PreAuthorize(“hasRole(‘ADMIN’)”) //不允许
@PreAuthorize(“hasAuthority(‘ADMIN’)”) //允许
@PreAuthorize(“hasRole(‘ROLE_ADMIN’)”) //不允许
@PreAuthorize(“hasAuthority(‘ROLE_ADMIN’)”) //不允许

身份加ROLE_前缀的好处,可以通过hasRole授权

身份ROLE_ADMIN能通过的权限 @PreAuthorize(“hasRole(‘ROLE_ADMIN’)”) //允许
@PreAuthorize(“hasAuthority(‘ROLE_ADMIN’)”) //允许
@PreAuthorize(“hasRole(‘ADMIN’)”) //允许
@PreAuthorize(“hasAuthority(‘ADMIN’)”) //不允许

总结:hasAuthority能通过的身份必须字符串完全一模一样,而hasRole能通过的身份必须前缀带有ROLE_,同时可以通过两种字符串,一是带有前缀ROLE_,二是不带前缀ROLE_。

二者的源码分析:

		public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
			return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
		}


	private String rolePrefix = "ROLE_";
	
		public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
			return access(withRoleHierarchy(AuthorityAuthorizationManager
				.hasAnyRole(AuthorizeHttpRequestsConfigurer.this.rolePrefix, new String[] { role })));
		}



	public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String rolePrefix, String[] roles) {
		Assert.notNull(rolePrefix, "rolePrefix cannot be null");
		Assert.notEmpty(roles, "roles cannot be empty");
		Assert.noNullElements(roles, "roles cannot contain null values");
		return hasAnyAuthority(toNamedRolesArray(rolePrefix, roles));
	}


	private static String[] toNamedRolesArray(String rolePrefix, String[] roles) {
		String[] result = new String[roles.length];
		for (int i = 0; i < roles.length; i++) {
			String role = roles[i];
			Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> role + " should not start with "
					+ rolePrefix + " since " + rolePrefix
					+ " is automatically prepended when using hasAnyRole. Consider using hasAnyAuthority instead.");
			result[i] = rolePrefix + role;
		}
		return result;
	}


	/**
	 * Creates an instance of {@link AuthorityAuthorizationManager} with the provided
	 * authorities.
	 * @param authorities the authorities to check for
	 * @param <T> the type of object being authorized
	 * @return the new instance
	 */
	public static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... authorities) {
		Assert.notEmpty(authorities, "authorities cannot be empty");
		Assert.noNullElements(authorities, "authorities cannot contain null values");
		return new AuthorityAuthorizationManager<>(authorities);
	}

能看到,当使用hasRole()的时候,会给传入的字符串加上ROLE_前缀。这就要求,不管在代码中有没有设置带前缀的,数据库中都需要带ROLE_前缀


解决方案:

将代码中设置成hasAuthority

springsecurityConfig中

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        //首页所有人都可以访问,功能页只有有权限的人才能访问
        httpSecurity
                .authorizeHttpRequests((requests) -> requests
                                .requestMatchers("/","/index").permitAll()
                                .requestMatchers("/toLogin","/toLoginPage").permitAll()
                        //hasAuthority判断的时候,数据库里和设置的需要一模一样的
                                .requestMatchers("/level1/**","/level2/**","/level3/**").hasAuthority("root")
                                .requestMatchers("/level2/**").hasAuthority("user")
                                //对静态资源的过滤
                                .requestMatchers("/css/**","/js/**","/views/**").permitAll()
                                .anyRequest().authenticated()
                        //没有权限默认回到登陆页面,也可以不传参数,默认是login.html页面
                ).formLogin(form -> form.loginPage("/toLoginPage")
                //处理前端跳转页面,要跟form表单里的action相同
                //不配置会走自己设置的controller
                        .loginProcessingUrl("/toLogin")
                //配置了不管是什么url,认证完成都会走/success
//                        .successForwardUrl("/success")
                        .passwordParameter("pwd")
                        .usernameParameter("username"))
                .csrf(csrf -> csrf.disable());
        return httpSecurity.build();
    }
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>首页</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
    <link th:href="@{/css/qinstyle.css}" rel="stylesheet">
</head>

<body>

<!--主容器-->
<div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item"  th:href="@{/index}">首页</a>

            <!--登录注销-->
            <div class="right menu">
                <!--!isAuthenticated()如果未登录-->
                <div sec:authorize="!isAuthenticated()">
                    <a class="item" th:href="@{/toLoginPage}">
                        <i class="address card icon"></i> 登录
                    </a>
                </div>


                <!--如果已登录:用户名,注销-->
                <div sec:authorize="isAuthenticated()">
                    <a class="item">
                        用户名:<span sec:authentication="name"> </span>
<!--                        principal.authorities获得用户的权限-->
                        角色:<span sec:authentication="principal.authorities"> </span>
                    </a>
                </div>
                <div sec:authorize="isAuthenticated()">
<!--                    如果想改成post,需要改成表单form才行-->
                    <a class="item" th:href="@{/logout}">
                        <i class="sign-out icon"></i> 注销
                    </a>
                </div>

                <!--已登录
                <a th:href="@{/usr/toUserCenter}">
                    <i class="address card icon"></i> admin
                </a>
                -->
            </div>
        </div>
    </div>

    <div class="ui segment" style="text-align: center">
        <h3>Spring Security Study by 秦疆</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">
            <!--菜单根据用户的角色动态的实现-->
            <div class="column" sec:authorize="hasAnyAuthority('root','user')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 1</h5>
                            <hr>
                            <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
                            <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
                            <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column" sec:authorize="hasAuthority('root')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="column" sec:authorize="hasAuthority('root')">
            <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 3</h5>
                            <hr>
                            <div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
                            <div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
                            <div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>

</div>


<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/js/semantic.min.js}"></script>

</body>
</html>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值