spring security角色和权限,不能同时调用.roles().authorities()

概述

通过下面实验说明问题

实现配置类

同时授予角色和权限,关键代码:

.roles("ADMIN")//admin角色,角色一般全大写
.authorities("mng_user");//管理user权限,权限一般小写

完整代码:

package top.malaoshi.stdsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig2 extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//      将用户名、密码放入内存中
        auth.inMemoryAuthentication()
//                .passwordEncoder(NoOpPasswordEncoder.getInstance())//密码为明文
                .withUser("admin")//用户名
                .password("{noop}123456")//{noop}表示密码为明文,与上面是同一个含义
                .roles("ADMIN")//admin角色,角色一般全大写
                .authorities("mng_user");//管理user权限,权限一般小写
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**","/js/**","/img/**");//绕开所有的filter,直接跳过验证
    }

    //    安全拦截机制
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()//禁用csrf
//                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
//                .and()

                .authorizeRequests()//url权限配置
//                .antMatchers("/index.html").permitAll() // 匹配这些路径可以所有人访问
//                .antMatchers("/user/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAuthority("mng_user")
                .anyRequest().authenticated()
                .and()

                //login部分
                .formLogin()
                .loginPage("/login.html")//指定登录页面。所有url前面必须写/,否则报错 'xxx' is not a valid forward URL
                .loginProcessingUrl("/login")//security提供的处理登录url,默认是/login
//                .successForwardUrl("/index.html")//登录成功后跳转的页面,需要额外处理才能使用,否则页面会提示错误
                .defaultSuccessUrl("/index.html")//登录成功后重定向的页面
                .failureUrl("/login_fails.html")//登录失败跳转的页面
//                .failureForwardUrl("/login_fails.html")//登录失败不会转发
                .permitAll()//让login部分的url可以任意访问
//
//                //logout部分
                .and()
                .logout()
                .logoutSuccessUrl("/login.html")//退出成功访问的页面
                .invalidateHttpSession(true)//清空session
                .permitAll();//让logout部分的url可以任意访问

    }

}

Controller

同上

测试

登录后访问Controller,打印如下:

org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: mng_user

发现只有权限Granted Authorities: mng_user

原因

执行下面代码时,

.roles("ADMIN")//admin角色,角色一般全大写
.authorities("mng_user");//管理user权限,权限一般小写

后一句的权限把前一句的角色覆盖了

.roles("ADMIN")源码

调用.roles("ADMIN")时,执行org.springframework.security.core.userdetails.User类的下面方法:

public User.UserBuilder roles(String... roles) {
            List<GrantedAuthority> authorities = new ArrayList(roles.length);
            String[] var3 = roles;
            int var4 = roles.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                String role = var3[var5];
                Assert.isTrue(!role.startsWith("ROLE_"), () -> {
                    return role + " cannot start with ROLE_ (it is automatically added)";
                });
                authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
            }

            return this.authorities((Collection)authorities);
        }

然后调用了下面方法,

public User.UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
    this.authorities = new ArrayList(authorities);
    return this;
}

注意: 创建ArrayList并赋给成员变量authorities

.authorities("mng_user")源码

最终调用下面源码:

public User.UserBuilder authorities(Collection<? extends GrantedAuthority> authorities) {
    this.authorities = new ArrayList(authorities);
    return this;
}

和上面是同一个方法,创建ArrayList并赋给成员变量authorities

结论

所以调用下面方法时,后一行代码会覆盖前一行代码

.roles("ADMIN")//admin角色,角色一般全大写
.authorities("mng_user");//管理user权限,权限一般小写

总结

在开发中,如果有角色和权限概念,推荐调用.authorities("mng_user","ROLE_ADMIN");,将角色全大写,有前缀:ROLE_

千万不要调用下面代码:

.roles("ADMIN")//admin角色,角色一般全大写
.authorities("mng_user");//管理user权限,权限一般小写

后一行代码会覆盖前一行


原文出处:/show_1EF60Rbhbjun.html