zoukankan      html  css  js  c++  java
  • 将 Shiro 作为应用的权限基础 三:基于注解实现的授权认证过程

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限。 

    如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限等等。 

     

    一、用户权限模型

    为实现一个较为灵活的用户权限数据模型,通常把用户信息单独用一个实体表示,用户权限信息用两个实体表示。

    1. 用户信息用 LoginAccount 表示,最简单的用户信息可能只包含用户名 loginName 及密码 password 两个属性。实际应用中可能会包含用户是否被禁用,用户信息是否过期等信息。
    2. 用户权限信息用 Role 与 Permission 表示,Role 与 Permission 之间构成多对多关系。Permission 可以理解为对一个资源的操作,Role 可以简单理解为 Permission 的集合。
    3. 用户信息与 Role 之间构成多对多关系。表示同一个用户可以拥有多个 Role,一个 Role 可以被多个用户所拥有。

     


     

    权限声明及粒度 

    Shiro权限声明通常是使用以冒号分隔的表达式。就像前文所讲,一个权限表达式可以清晰的指定资源类型,允许的操作。同时,Shiro权限表达式支持简单的通配符,可以更加灵活的进行权限设置。 

    下面以实例来说明权限表达式。 

    可查询用户数据 

    User:view 

    可查询或编辑用户数据 

    User:view,edit 

    可对用户数据进行所有操作 

    User:*或 user 

    可编辑id为123的用户数据 

    User:edit:123 

     

    授权处理过程

    认证通过后接受 Shiro 授权检查,授权验证时,需要判断当前角色是否拥有该权限。

    只有授权通过,才可以访问受保护 URL 对应的资源,否则跳转到“未经授权页面”。

    如果我们自定义Realm实现,比如我后面的例子中,自定义了ShiroDbRealm类,当访问ShiroDbRealm.doGetAuthorizationInfo()进行授权。

    @Controller
    @RequestMapping(value = "/user")
    public class UserController {
     
    @Resource(name="userService")
    private IUserService userService;
     
    /**
     * 测试权限
     * 只有拥有 user:create权限,才能进行注册
     * @param user
     * @return
     */
    @RequestMapping(value = "/register")
    @ResponseBody
    @RequiresPermissions("user:create")
    public boolean register(User user){
    return userService.register(user);
    }



     

     

    二、授权实现 

    Shiro支持三种方式实现授权过程: 

    • 编码实现
    • 注解实现
    • JSP Taglig实现

     

    1、基于编码的授权实现 

    1、基于权限对象的实现 

    创建org.apache.shiro.authz.Permission的实例,将该实例对象作为参数传递给Subject.isPermitted()进行验证。 

          

    Permission printPermission = new PrinterPermission("laserjet4400n", "print");  
    Subject currentUser = SecurityUtils.getSubject();  
    if (currentUser.isPermitted(printPermission)) {  
        //show the Print button  
    } else {  
        //don't show the button?  Grey it out?  
    }  

     

     

    2基于字符串的实现 

    相比笨重的基于对象的实现方式,基于字符串的实现便显得更加简洁。 

           

    Subject currentUser = SecurityUtils.getSubject();  
    if (currentUser.isPermitted("printer:print:laserjet4400n")) {  
        //show the Print button  
    } else {  
        //don't show the button?  Grey it out?  
    }  

     

    使用冒号分隔的权限表达式是org.apache.shiro.authz.permission.WildcardPermission默认支持的实现方式。 

    这里分别代表了资源类型:操作:资源ID 

     

    2、基于注解的授权实现 

    Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。 

     

    相关的注解: 

    @RequiresAuthentication 

    可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。  

            

    @RequiresAuthentication  
    public void updateAccount(Account userAccount) {  
        //this method will only be invoked by a   
        //Subject that is guaranteed authenticated  
        ...  
    } 

     

    @RequiresPermissions 

    当前用户需拥有制定权限 

           

    @RequiresPermissions("account:create")  
    public void createAccount(Account account) {  
        //this method will only be invoked by a Subject  
        //that is permitted to create an account  
        ...  
    } 


     

     

    3、基于JSP TAG的授权实现 

    Shiro提供了一套JSP标签库来实现页面级的授权控制。 

    在使用Shiro标签库前,首先需要在JSP引入shiro标签: 


    hasRole标签 

    验证当前用户是否属于该角色

     

    <shiro:hasRole name="administrator">  
        <a href="admin.jsp">Administer the system</a>  
    </shiro:hasRole>  

     

    hasPermission标签 

    验证当前用户是否拥有制定权限 

     

    <shiro:hasPermission name="user:create">  
        <a href="createUser.jsp">Create a new User</a>  
    </shiro:hasPermission>  



     

    三、Shiro授权的内部处理机制 


     

    1、在应用程序中调用授权验证方法(Subject的isPermitted*或hasRole*等) 

    2、Sbuject会委托应用程序设置的securityManager实例调用相应的isPermitted*或hasRole*方法。 

    3、接下来SecurityManager会委托内置的Authorizer的实例(默认是ModularRealmAuthorizer类的实例,类似认证实例)调用相应的授权方法。 

    4、每一个Realm将检查是否实现了相同的Authorizer 接口。然后,将调用Reaml自己的相应的授权验证方法。 

     

    四、授权代码

    UserController:处理用户登录后的请求(注册)

        

    		package org.shiro.demo.controller;
    		
    		import javax.annotation.Resource;
    		
    		import org.apache.shiro.authz.annotation.RequiresPermissions;
    		import org.apache.shiro.authz.annotation.RequiresRoles;
    		import org.shiro.demo.entity.User;
    		import org.shiro.demo.service.IUserService;
    		import org.springframework.stereotype.Controller;
    		import org.springframework.web.bind.annotation.RequestMapping;
    		import org.springframework.web.bind.annotation.ResponseBody;
    		
    		@Controller
    		@RequestMapping(value = "/user")
    		public class UserController {
    			
    			@Resource(name="userService")
    			private IUserService userService;
    		
    			/**
    			 * 测试权限
    			 * 只有拥有 user:create 权限,才能进行注册
    			 * @param user
    			 * @return
    			 */
    			@RequestMapping(value = "/register")
    			@ResponseBody
    			@RequiresPermissions("user:create")
    			public boolean register(User user){
    				return userService.register(user);
    			}
    			
    			/**
    			 * 测试角色
    			 * 只有拥有 administrator 角色,才能跳转到register页面
    			 * @return
    			 */
    			@RequestMapping(value = "/toRegister")
    			@RequiresRoles("administrator")
    			public String toRegister(){
    				return "/system/user/register";
    			}
    		}
    

     

     

    ShiroDbRealm:自定义的指定Shiro验证用户授权的类

    packageorg.shiro.demo.service.realm;
     
    importjava.util.ArrayList;
    importjava.util.List;
     
    importjavax.annotation.Resource;
     
    importorg.apache.commons.lang.StringUtils;
    importorg.apache.shiro.authc.AuthenticationException;
    importorg.apache.shiro.authc.AuthenticationInfo;
    importorg.apache.shiro.authc.AuthenticationToken;
    importorg.apache.shiro.authc.SimpleAuthenticationInfo;
    importorg.apache.shiro.authc.UsernamePasswordToken;
    importorg.apache.shiro.authz.AuthorizationException;
    importorg.apache.shiro.authz.AuthorizationInfo;
    importorg.apache.shiro.authz.SimpleAuthorizationInfo;
    importorg.apache.shiro.realm.AuthorizingRealm;
    importorg.apache.shiro.subject.PrincipalCollection;
    importorg.shiro.demo.entity.Permission;
    importorg.shiro.demo.entity.Role;
    importorg.shiro.demo.entity.User;
    importorg.shiro.demo.service.IUserService;
     
    /**
     * 自定义的指定Shiro验证用户登录的类
     * @author TCH
     *
     */
    publicclass ShiroDbRealm extends AuthorizingRealm{
     
    //@Resource(name="userService")
    privateIUserService userService;
     
    publicvoid setUserService(IUserService userService) {
    this.userService= userService;
    }
     
        /**
         * 为当前登录的Subject授予角色和权限
         * @see 经测试:本例中该方法的调用时机为需授权资源被访问时
         * @see经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例未启用AuthorizationCache
         * @seeweb层可以有shiro的缓存,dao层可以配有hibernate的缓存(后面介绍)
         */
    protectedAuthorizationInfo doGetAuthorizationInfo(
    PrincipalCollectionprincipals) {
     
    //获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next() 
    Stringaccount = (String) super.getAvailablePrincipal(principals);
     
    List<String>roles = new ArrayList<String>(); 
    List<String>permissions = new ArrayList<String>();
     
    //从数据库中获取当前登录用户的详细信息 
    Useruser = userService.getByAccount(account);
     
    if(user!= null){
    //实体类User中包含有用户角色的实体类信息 
    if(user.getRoles() != null && user.getRoles().size() > 0) {
    //获取当前登录用户的角色
    for(Role role : user.getRoles()) {
    roles.add(role.getName());
     //实体类Role中包含有角色权限的实体类信息 
    if(role.getPmss() != null && role.getPmss().size() > 0) {
     //获取权限 
    for(Permission pmss : role.getPmss()) {
    if(!StringUtils.isEmpty(pmss.getPermission())){
    permissions.add(pmss.getPermission());
    }
    }
    }
    }
    }
    }else{
    thrownew AuthorizationException();
    }
     
    //为当前用户设置角色和权限
    SimpleAuthorizationInfoinfo = new SimpleAuthorizationInfo();
    info.addRoles(roles);
            info.addStringPermissions(permissions);
           
    returninfo;
     
    }
     
    }

     

  • 相关阅读:
    C语言关键字
    C语言返回值
    五、Vue:使用axios库进行get和post、用拦截器对请求和响应进行预处理、Mock(数据模拟)
    四、Vue过渡与动画、过渡css类名、自定义指定、过滤器
    三、深入Vue组件——Vue插槽slot、动态组件
    二、Vue组件(component):组件的相互引用、通过props实现父子组件互传值
    一、Vue环境搭建及基础用法
    Django(十三)状态保持 —— cookie与session+ajax异步请求+session记住登录状态+cookie记住登录名密码
    Django(十二)视图--利用jquery从后台发送ajax请求并处理、ajax登录案例
    Django(十一)视图详解:基本使用、登录实例、HttpReqeust对象、HttpResponse对象
  • 原文地址:https://www.cnblogs.com/hanxue112253/p/3850546.html
Copyright © 2011-2022 走看看