• Shiro授权管理


    一、授权

      授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。

    二、Shiro授权概念(RBAC)

    1,Subject

      主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。

    2,Resource

      在应用中用户可以访问的任何东西,比如访问JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。

    3,Permission

      安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)打印文档等等。

      权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例级别的)。

    4,Role

      角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:开发工程师、项目经理、技术总监、CTO等都是角色,不同的角色拥有一组不同的权限。

      隐式角色:即直接通过角色来验证用户有没有操作权限,如在应用中CTO、技术总监、开发工程师可以使用打印机,假设某天不允许开发工程师使用打印机,此时需要从应用中删除相应代码;再如在应用中CTO、技术总监可以查看用户、查看权限;突然有一天不允许技术总监查看用户、查看权限了,需要在相关代码中把技术总监角色从判断逻辑中删除掉;即粒度是以角色为单位进行访问控制的,粒度较粗;如果进行修改可能造成多处代码修改。

      显示角色:在程序中通过权限控制谁能访问某个资源,角色聚合一组权限集合;这样假设哪个角色不能访问某个资源,只需要从角色代表的权限集合中移除即可;无须修改多处代码;即粒度是以资源/实例为单位的;粒度较细。

    三、授权流程

    1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer。

    2、Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver把字符串转换成相应的Permission实例。

    3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限。

    4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer 进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。

    四、SpringBoot中Shiro授权Demo

     Config.java

     1 package io.guangsoft.erp.config;
     2 
     3 import io.guangsoft.erp.realm.Realm;
     4 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
     5 import org.apache.shiro.codec.Base64;
     6 import org.apache.shiro.mgt.RememberMeManager;
     7 import org.apache.shiro.mgt.SecurityManager;
     8 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
     9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    10 import org.apache.shiro.web.mgt.CookieRememberMeManager;
    11 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    12 import org.apache.shiro.web.servlet.SimpleCookie;
    13 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    14 import org.springframework.context.annotation.Bean;
    15 import org.springframework.context.annotation.Configuration;
    16 
    17 import java.util.LinkedHashMap;
    18 import java.util.Map;
    19 
    20 @Configuration
    21 public class Config {
    22 
    23     @Bean
    24     public Realm realmManager() {
    25         // 加密相关
    26         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    27         // 散列算法
    28         hashedCredentialsMatcher.setHashAlgorithmName("md5");
    29         // 散列次数
    30         hashedCredentialsMatcher.setHashIterations(2);
    31         Realm realm = new Realm();
    32         realm.setCredentialsMatcher(hashedCredentialsMatcher);
    33         return realm;
    34     }
    35 
    36     @Bean
    37     public RememberMeManager rememberMeManager() {
    38         CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
    39         //注入自定义cookie(主要是设置寿命, 默认的一年太长)
    40         SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    41         simpleCookie.setHttpOnly(true);
    42         //设置RememberMe的cookie有效期为7天
    43         simpleCookie.setMaxAge(604800);
    44         rememberMeManager.setCookie(simpleCookie);
    45         //手动设置对称加密秘钥,防止重启系统后系统生成新的随机秘钥,防止导致客户端cookie无效
    46         rememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));
    47         return rememberMeManager;
    48     }
    49 
    50     @Bean
    51     public SecurityManager securityManager() {
    52         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    53         securityManager.setRealm(realmManager());
    54         securityManager.setRememberMeManager(rememberMeManager());
    55         return securityManager;
    56     }
    57 
    58     //两个Bean开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
    59     @Bean
    60     public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
    61         DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    62         advisorAutoProxyCreator.setProxyTargetClass(true);
    63         return advisorAutoProxyCreator;
    64     }
    65 
    66     @Bean
    67     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    68         AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    69         authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    70         return authorizationAttributeSourceAdvisor;
    71     }
    72 
    73     @Bean
    74     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    75         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    76         shiroFilterFactoryBean.setSecurityManager(securityManager);
    77         //当用户未登录则跳转到登录路径
    78         shiroFilterFactoryBean.setLoginUrl("/login");
    79         //登录成功后要跳转的链接,表单登录方式有效
    80         shiroFilterFactoryBean.setSuccessUrl("/index");
    81         //未授权界面,指定没有权限操作时跳转页面
    82         shiroFilterFactoryBean.setUnauthorizedUrl("/warning");
    83         //配置不会被过滤的链接顺序判断,过虑器链定义,从上向下顺序执行,一般将/**放在最下边
    84         Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    85         //对静态资源设置匿名访问,anon:所有url都可以匿名访问
    86         filterChainDefinitionMap.put("/assets/**", "anon");
    87         //放开登录接口,允许进行登录操作
    88         filterChainDefinitionMap.put("/shiro/login", "anon");
    89         //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
    90         filterChainDefinitionMap.put("/shiro/logout", "logout");
    91         //authc:所有url都必须认证通过才可以访问
    92         filterChainDefinitionMap.put("/**", "authc");
    93         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    94         return shiroFilterFactoryBean;
    95     }
    96 
    97 }

    Realm.java

     1 package io.guangsoft.erp.realm;
     2 
     3 import org.apache.shiro.SecurityUtils;
     4 import org.apache.shiro.authc.AuthenticationException;
     5 import org.apache.shiro.authc.AuthenticationInfo;
     6 import org.apache.shiro.authc.AuthenticationToken;
     7 import org.apache.shiro.authc.SimpleAuthenticationInfo;
     8 import org.apache.shiro.authz.AuthorizationInfo;
     9 import org.apache.shiro.authz.SimpleAuthorizationInfo;
    10 import org.apache.shiro.realm.AuthorizingRealm;
    11 import org.apache.shiro.subject.PrincipalCollection;
    12 import org.apache.shiro.subject.Subject;
    13 import org.apache.shiro.util.ByteSource;
    14 
    15 import java.util.ArrayList;
    16 import java.util.List;
    17 
    18 public class Realm extends AuthorizingRealm {
    19 
    20     //认证
    21     @Override
    22     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    23         String userName = (String) token.getPrincipal();
    24         System.out.println("通过username在数据库中获取用户密码并设置加密盐.");
    25         String password = "4d521acb9b8b3b4fa082ab16b3bd363a";
    26         String salt = "guanghe";
    27         return new SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt), this.getName());
    28     }
    29 
    30     //授权
    31     @Override
    32     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    33         Subject subject = SecurityUtils.getSubject();
    34         String username = (String) subject.getPrincipal();
    35         System.out.println("通过username在数据库中获取用户角色与权限.");
    36         // 角色
    37         List<String> roles = new ArrayList<>();
    38         roles.add("admin");
    39         roles.add("user");
    40         // 权限
    41         List<String> permissions = new ArrayList<>();
    42         permissions.add("admin:select");
    43         permissions.add("admin:update");
    44         permissions.add("user:select");
    45         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    46         simpleAuthorizationInfo.addStringPermissions(permissions);
    47         simpleAuthorizationInfo.addRoles(roles);
    48         return simpleAuthorizationInfo;
    49     }
    50 
    51 }

    mainController.java

     1 package io.guangsoft.erp.controller;
     2 
     3 import org.apache.shiro.authz.annotation.Logical;
     4 import org.apache.shiro.authz.annotation.RequiresPermissions;
     5 import org.apache.shiro.authz.annotation.RequiresRoles;
     6 import org.springframework.stereotype.Controller;
     7 import org.springframework.web.bind.annotation.RequestMapping;
     8 
     9 @Controller
    10 public class mainController {
    11 
    12     @RequestMapping("index")
    13     public String index() {
    14         return "index.html";
    15     }
    16 
    17     @RequestMapping("login")
    18     public String login() {
    19         return "login.html";
    20     }
    21 
    22     @RequiresRoles(value = {"admin","user"},logical = Logical.AND)
    23     @RequiresPermissions(value={"user:select","admin:select"},logical = Logical.OR)
    24     @RequestMapping("select")
    25     public String select() {
    26         return "manage/select.html";
    27     }
    28 
    29     @RequiresRoles("operator")
    30     @RequestMapping("update")
    31     public String update() {
    32         return "manage/update.html";
    33     }
    34 
    35     @RequiresPermissions("admin:delete")
    36     @RequestMapping("delete")
    37     public String delete() {
    38         return "manage/delete.html";
    39     }
    40 
    41 }

     

  • 相关阅读:
    sql server 纵横表的转换
    url参数的编码解码Demo
    SqlServer 列的增加和删除
    asp.net下ajax.ajaxMethod使用方法(转)
    js中document.all 的用法
    cookie跨域,跨目录访问及单点登录。
    错误记录:html隐藏域的值存字符串时出错
    .NET下用C#实现邮箱激活功能
    js与C#服务端 json数据交互
    sqlserver数据可空插入报错
  • 原文地址:https://www.cnblogs.com/guanghe/p/10690109.html
Copyright © 2020-2023  润新知