分享

SpringSecurity授权详解

 昵称70680357 2020-06-30

 项目开发离不开认证授权,简单来说,认证解决你是谁的问题,授权解决你能干什么的问题。下面讲讲SpringSecurity的授权。

  一、授权基本知识

  1、授权因项目而异

  一些业务系统,如电商网站,只需区分是否登录,或者是普通用户还是VIP用户等基本角色,它们的权限基本不会改变,这种情况可以直接在代码中写死。还有一些内管系统,如运营人员管理系统 ,角色众多,权限复杂,权限规则随着公司和业务的发展不断变化,这种情况必须配置权限。

  2、什么是授权

  授权不是说在页面上隐藏某个连接或者按钮就完事儿,而是要判断当前用户有没有访问该连接的权限。授权模型中要明确两个要素:系统配置信息和用户权限信息。系统配置信息中记录了url连接和每一个连接需要的权限,比如www.a.com/user需要A权限;用户权限信息则记录了某用户具有的权限,比如用户张三具有A、B、C权限。当一个用户发了一个连接请求,系统会拿这两份信息比对,如果这个请求需要A权限,发请求的这个用户也有A权限,那么就可以访问。

  二、SpringSecurity中的授权

  1、授权之是否需要登录

  有一些链接需要登录才能访问,也有一些链接无需登录就能访问,怎么允许链接不登录就能访问呢?SpringSecurity配置如下:

复制代码
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
            .loginPage("/authentication/require")
            .loginProcessingUrl("/authentication/form")
        http.authorizeRequests()
            .antMatchers(
                    "/login.html",
                    "/authentication/require",
                    "/code/image",
                    "/session/invalid",
                    "/logout.html"
                    ).permitAll()//不需要身份认证
            .anyRequest()
            .authenticated();
        http.csrf().disable();
    }
}
复制代码

  2、授权之区分简单角色

  链接"/user"需要"ADMIN"权限才能访问,配置如下:

复制代码
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
            .loginPage("/authentication/require")
            .loginProcessingUrl("/authentication/form")
        http.authorizeRequests()
            .antMatchers(
                    "/login.html",
                    "/authentication/require",
                    "/code/image",
                    "/session/invalid",
                    "/logout.html"
                    ).permitAll()
            .antMatchers("/user").hasRole("ADMIN")
            .anyRequest()
            .authenticated();
        http.csrf().disable();
    }
}
复制代码
复制代码
@Component
public class MyUserDetailsService implements UserDetailsService{

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, passwordEncoder.encode("123456"), 
            AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}
复制代码

  说明:

  c)注意,hasRole("ADMIN")对应的是“ROLE_ADMIN”,大小写敏感。必须有“ROLE_”。

  

  

  3、授权之权限表达式

  上面讲了permitAll和hasRole,SpringSecurity还有一些其他的表达式如下:

  

  说明:

  a)anonymous是匿名,即不登录时可以访问。

  b).antMatchers("/user").access("hasRole('ADMIN') and hasIpAddress('192.168.1.0/24')") ;ADMIN权限和IP地址同时满足。

  c)判断hasRole中的权限时,在设置权限时添加前缀ROLE_,其它表达式时不需要添加。

  4、将授权提取到AuthorizeConfigProvider中统一管理

  从上面的示例中看到授权代码写在WebSecurityConfigurerAdapter中,耦合度高,下面解决这个问题

  1)AuthorizeConfigProvider.java

public interface AuthorizeConfigProvider {
    void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}

  2)MyAuthorizeConfigProvider.java,配置通用url

复制代码
@Component
public class MyAuthorizeConfigProvider implements AuthorizeConfigProvider {

    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        config.antMatchers(
                "/login.html",
                "/authentication/require",
                "/code/image",
                "/session/invalid",
                "/logout.html"
        ).permitAll();
语言 方法
8748 swPeDZIxo0
5v217 成版人抖音app
1210 2011-07-02 13:20:07
} }
复制代码

  3)DempAuthorizeConfigProvider.java,配置自定义url

复制代码
@Component
public class DempAuthorizeConfigProvider implements AuthorizeConfigProvider {

    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        config.antMatchers("/user",
                           "/demo.html"
                ).hasRole("ADMIN");
    }
}
复制代码

  4)AuthorizeConfigManager.java

public interface AuthorizeConfigManager {
    void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}

  5)MyAuthorizeConfigManager.java

复制代码
@Component
public class MyAuthorizeConfigManager implements AuthorizeConfigManager {

    //Spring启动的时候,所有AuthorizeConfigProvider的接口的实现都(自动)放在AuthorizeConfigProviders中
    @Autowired
    private Set<AuthorizeConfigProvider> AuthorizeConfigProviders;
    
    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        for(AuthorizeConfigProvider authorizeConfigProvider:AuthorizeConfigProviders) {
            authorizeConfigProvider.config(config);
        }
        config.anyRequest().authenticated();//所有请求需要身份认证
    }
}
复制代码

  上面的代码实现了权限代码的解耦,现在看一下WebSecurityConfigurerAdapter中的配置:

复制代码
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    ... ...
    @Autowired
    private AuthorizeConfigManager authorizeConfigManager;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ... ...
        /**http.authorizeRequests()
            .antMatchers(
                    "/login.html",
                    "/authentication/require",
                    "/code/image",
                    "/session/invalid",
                    "/logout.html"
                    ).permitAll()//该路径不需要身份认证
            .antMatchers("/user").hasRole("ADMIN")
            //.antMatchers(HttpMethod.GET,"/user/*").hasRole("ADMIN")//GET请求需要这个权限
            //.antMatchers("/user").access("hasRole('ADMIN') and hasIpAddress('192.168.1.0/24')")  
            .anyRequest()
            .authenticated();*/
        http.csrf().disable();//先禁止掉跨站请求伪造防护功能
        authorizeConfigManager.config(http.authorizeRequests());//将授权代码提取到AuthorizeConfigManager中
    }
}
复制代码

  5、通用RBAC(Role-Based Access Control)数据模型

  上面的授权都是静态的,即都是写死在代码中的,遇到复杂的权限管理,都是配置在数据库中的,一般由五张表组成,即RBAC。

  1)RBAC数据模型的五张表

  用户表,存储用户信息,由业务人员维护;

  角色表,存储角色信息,由业务人员维护 ;

  资源表,存储资源信息(菜单、按钮及其URL),由开发人员维护;

  用户-角色关系表,存储用户和角色的对应关系,多对多,由业务人员维护;

  角色-资源关系表,存储角色和资源的对应关系 ,多对多,由业务人员维护;

  2)代码实现

  RbacService.java

public interface RbacService {
    boolean hasPermission(HttpServletRequest request,Authentication authentication);
}

  RbacServiceImpl.java

复制代码
@Component("rbacService")
public class RbacServiceImpl implements RbacService{
private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public boolean hasPermission(HttpServletRequest request, Authentication authentication) { System.out.println("---------进入hasPermission方法:url"+request.getRequestURI()); Object principal = authentication.getPrincipal(); boolean hasPermission = false; if(principal instanceof UserDetails) { String username = ((UserDetails)principal).getUsername(); //根据username,读取用户所拥有权限的所有url Set<String> urls = new HashSet<>(); urls.add("/user"); urls.add("/index.html"); for(String url:urls) { if(antPathMatcher.match(url, request.getRequestURI())) { hasPermission = true; break; } } } return hasPermission; } }
复制代码

  修改DempAuthorizeConfigProvider.java

复制代码
@Component
@Order(Integer.MAX_VALUE)//指定顺序,最后读取
public class DempAuthorizeConfigProvider implements AuthorizeConfigProvider {
@Override public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) { /**config.antMatchers("/user", "/demo.html" ).hasRole("ADMIN"); */ config.anyRequest().access("@rbacService.hasPermission(request,authentication)"); //anyRequest放到最后读,注释掉MyAuthorizeConfigManager中的anyRequest,否则会覆盖此处的anyRequest } }
复制代码

  修改MyAuthorizeConfigProvider.java

复制代码
@Component
@Order(Integer.MIN_VALUE)//指定顺序,先读取
public class MyAuthorizeConfigProvider implements AuthorizeConfigProvider {

    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        config.antMatchers(
                "/login.html",
                "/authentication/require",
                "/code/image",
                "/session/invalid",
                "/logout.html"
        ).permitAll();
    }
}
复制代码

  修改MyAuthorizeConfigManager.java

复制代码
@Component
public class MyAuthorizeConfigManager implements AuthorizeConfigManager {

    //Spring启动的时候,所有AuthorizeConfigProvider的接口的实现都(自动)放在AuthorizeConfigProviders中
    @Autowired
    //private Set<AuthorizeConfigProvider> AuthorizeConfigProviders;
    private List<AuthorizeConfigProvider> AuthorizeConfigProviders;//改为有序集合
    
    @Override
    public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
        for(AuthorizeConfigProvider authorizeConfigProvider:AuthorizeConfigProviders) {
            authorizeConfigProvider.config(config);
        }
        //config.anyRequest().authenticated();//所有请求需要身份认证,注销掉,因为DempAuthorizeConfigProvider中有anyRequest了
    }
}
复制代码

  说明:

  a)通过设置@Order注解,使通用url先读取,首选执行permitAll;自定义url后读取,执行rbacService.hasPermission方法。

  b)DempAuthorizeConfigProvider中指定了anyRequest访问hasPermission方法,MyAuthorizeConfigManager中去掉最后一句。

  c)MyAuthorizeConfigManager中改为有序集合List,先实现AuthorizeConfigProvider的bean先被读取,与@Order对应。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多