美文网首页
CRM项目03-shiro02

CRM项目03-shiro02

作者: 建国同学 | 来源:发表于2020-05-20 11:48 被阅读0次

    一、基于 Shiro 的权限加载

    Controller 方法上贴 Shiro 提供的权限注解

    (@RequiresPermissions)

    @RequiresRoles("ADMIN")
    //权限表达式可以配置多个,默认是&&,改为or相当于||, 要求同时拥有多个权限才能访问该方法
    @RequiresPermissions(value = {"role:list","角色列表"},logical = Logical.OR) 
    

    开启 Shiro 注解扫描器

    需要applicationContext.xml配置<aop:config>才会创建处理对象
    shiro.xml

     <!-- 开启 Shiro 注解扫描器,对贴了 shiro 注解的类进行代理,利用代理的方式来实现权限拦截的功能
        需要applicationContext.xml配置<aop:config>才会创建..advisor这样的处理bean的对象 -->
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>
    

    生成权限信息方法

     @Override
        public void reload(){
            // 获取数据库中所有的权限表达式
            List<String> expressions = permissionMapper.selectAllExpression();// role:delete   role:list
    
            // 从spring 容器中获取所有的controller
            Map<String, Object> beans = ctx.getBeansWithAnnotation(Controller.class);
            // 获取所有的value值
            Collection<Object> values = beans.values();
            // 遍历循环获取每一个controller
            for (Object controller : values){
                // 判断实例是否是Cglib的代理对象
                if (!AopUtils.isCglibProxy(controller)) {
                    continue; // 执行下一次循环
                }
                // 获取controller 的字节码对象
                // isCglibProxy已解决=>SuperClass获取该类的父类代理类,就可以获取到代理的注解,其他类获取到父类Object,就拿不到注解,annotation=null
                Class<?> clazz = controller.getClass().getSuperclass();
                // 获取controller 的所有方法
                Method[] methods = clazz.getDeclaredMethods();
                // 遍历出每一个方法
                for (Method method:methods) {
                    // 判断是否有贴自定义的权限注解
                    RequiresPermissions annotation = method.getAnnotation(RequiresPermissions.class);
                    // 如果有贴,封装成对象,并插入数据库
                    if(annotation != null){
                        // 获取权限表达式
                        String expression = annotation.value()[0];
                        // 通过反射拼接权限表达式
                        /*String expression = clazz.getSimpleName();  // DepartmentController
                        expression = expression.replace("Controller",""); // Department
                        expression = StringUtils.uncapitalize(expression);  // department
                        expression = expression + ":" + method.getName(); // department:list*/
    
                        // 判断该表达式是否已经存在数据库中,不存在就插入
                        if(!expressions.contains(expression)) {
                            Permission permission = new Permission();
                            permission.setName(annotation.value()[1]); // 中文的权限名称
                            permission.setExpression(expression); // 英文 权限表达式
                            permissionMapper.insert(permission);
                        }
                    }
                    // 如果没有就不处理
                }
            }
    
        }
    

    二、Shiro 基于web 环境的授权

    hiro.java

    获取授权进行处理

        /**
         * 授权,代码中需要 判断授权 hasxxx的时候才会执行
         *
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("=======================");
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 获取当前登录用户
            Subject subject = SecurityUtils.getSubject();
            // 获取主体的身份信息
            Employee employee = (Employee) subject.getPrincipal();// new SimpleAuthenticationInfo(传第一个参数)
            // 判断是否是管理员
            if (employee.isAdmin()) {
                info.addRole("ADMIN");// 方便后续判断是否管理员可以直接用submit来判断
                info.addStringPermission("*:*"); // *:* 表示所有权限,即不做任何限制
            } else {
                // 根据用户的id查询该用户拥有的角色
                List<Role> roles = roleMapper.selectByEmpId(employee.getId());
                ArrayList<String> roleSnList = new ArrayList<>();
                for (Role role : roles) {
                    roleSnList.add(role.getSn()); // 放入角色的编码
                }
                info.addRoles(roleSnList);
                // 根据用户的id查询该用户拥有的权限
                List<String> permission = permissionMapper.selectByEmpId(employee.getId());
                info.addStringPermissions(permission);
            }
            return info;
        }
    

    登录控制器

    LoginController.java

     @RequestMapping("/login")
        @ResponseBody
        public JsonResult login(String username, String password){
            try {
                // 封装令牌
                UsernamePasswordToken token = new UsernamePasswordToken(username, password);
                // 利用shiro的api来进行登录
                SecurityUtils.getSubject().login(token);
                return new JsonResult(); // 默认为true
                
            }catch (UnknownAccountException e){
                e.printStackTrace();
                return new JsonResult(false,"账号不存在");
            }catch (IncorrectCredentialsException e){
                e.printStackTrace();
                return new JsonResult(false,"密码错误");
            }catch (DisabledAccountException e){
                e.printStackTrace();
                return new JsonResult(false,"账号已禁用,请联系管理员");
            }catch (Exception e){
                e.printStackTrace();
                return new JsonResult(false,"登录异常,请联系管理员");
            }
        }
    
      /* @RequestMapping("/logout")
            在 shiro过滤器中配置
           /logout.do=logout
       */
    

    异常捕获

    • @ControllerAdvice:Controller 增强器
      通常和@ExceptionHandler 注解配合使用,其中最常用的是@ExceptionHandler,对 Controller 中
      的异常作特定处理。
    • @ExceptionHandler:异常处理器
      贴在方法上,当 Controller 中配置指定异常时,会执行贴了该注解的方法。

    ContrllerExceptionHandler.java

    /**
     * 对控制器进行处理
     * 利用aop
     */
    // mvc.xml 的扫描控制器 上一定要扫到
    @ControllerAdvice
    public class ContrllerExceptionHandler {
    
        /**
         * 当前的方法用于捕获指定的异常
         */
        @ExceptionHandler(RuntimeException.class) // 规定为运行时异常
        public String handler(RuntimeException e, HttpServletResponse response, HandlerMethod handlerMethod) throws IOException {
            // 打印错误信息方便debug
            e.printStackTrace();
            // 判断如果是ajax对应的方法(判断有没有ResponseBody注解),有就返回JsonResult
            if(handlerMethod.hasMethodAnnotation(ResponseBody.class)){
                response.setContentType("application/json;charset=utf-8"); // 为了适配浏览器,显式的告诉浏览器数据类型和编码方式
                response.getWriter().print(JSON.toJSONString(new JsonResult(false, "操作失败")));
                return null;
            }else {
                // 如果不是,就返回错误的视图页面
                return "common/error";
            }
        }
    
        /**
         * 捕获没有权限的异常
         * @param e
         * @param response
         * @param handlerMethod
         * @return
         * @throws IOException
         */
        @ExceptionHandler(UnauthorizedException.class) // 规定为运行时异常
        public String handlerUnauthorized(RuntimeException e, HttpServletResponse response, HandlerMethod handlerMethod) throws IOException {
            // 打印错误信息方便debug
            e.printStackTrace();
            // 判断如果是ajax对应的方法(判断有没有ResponseBody注解),有就返回JsonResult
            if(handlerMethod.hasMethodAnnotation(ResponseBody.class)){
                response.setContentType("application/json;charset=utf-8"); // 为了适配浏览器,显式的告诉浏览器数据类型和编码方式
                response.getWriter().print(JSON.toJSONString(new JsonResult(false, "您没有该权限")));
                return null;
            }else {
                // 如果不是,就返回错误的视图页面
                return "common/nopermission";
            }
        }
    
    
    }
    

    三、 Shiro 标签

    依赖

    <dependency>
    <groupId>net.mingsoft</groupId>
    <artifactId>shiro-freemarker-tags</artifactId>
    <version>1.0.0</version>
    </dependency>
    

    注册 shiro 的标签

    MyFreeMarkerConfig.java

    public class MyFreeMarkerConfig extends FreeMarkerConfigurer {
        @Override
        public void afterPropertiesSet() throws IOException, TemplateException {
            //继承之前的属性配置,这不不能省
            super.afterPropertiesSet();
            Configuration cfg = this.getConfiguration();
            cfg.setSharedVariable("shiro", new ShiroTags());//shiro 标签
        }
    }
    

    配置到当前环境

    将 shiro 的标签设置成当前环境中使用的配置对象
    mvc.xml

    <!-- 注册FreeMarker配置类(配置自定义的配置类,带有shiro标签的功能) -->
        <bean class="cn.wolfcode.shiro.MyFreeMarkerConfig">
            <!-- 配置 freemarker 的文件编码 -->
            <property name="defaultEncoding" value="UTF-8"/>
            <!-- 配置 freemarker 寻找模板的路径 -->
            <property name="templateLoaderPath" value="/WEB-INF/views/"/>
        </bean>
    

    shiro 的 freemarker 常用标签:

    1. authenticated 标签:已认证通过的用户。
      <@shiro.authenticated> </@shiro.authenticated>
    2. notAuthenticated 标签:未认证通过的用户。与 authenticated 标签相对。
      <@shiro.notAuthenticated></@shiro.notAuthenticated>
    3. principal 标签:输出当前用户信息,通常为登录帐号信息
      <@shiro.principal property="name" />
      后台是直接将整个员工对象作为身份信息的
      return new SimpleAuthenticationInfo(employee,employee.getPassword(),ByteSource.Util.bytes(username),"CrmRealm");
    <@shiro.authenticated>
        <@shiro.principal property="name"/>
    </@shiro.authenticated>
    
    1. hasRole 标签:验证当前用户是否属于该角色 ,
      <@shiro.hasRolename=”admin”>Hello admin!</@shiro.hasRole>
    2. hasAnyRoles 标签:验证当前用户是否属于这些角色中的任何一个,角色之间逗号分隔 ,
      <@shiro.hasAnyRoles name="admin,user,operator">Hello admin! </@shiro.hasAnyRoles>
    3. hasPermission 标签:验证当前用户是否拥有该权限 ,
      <@shiro.hasPermissionname="/order:*">订单/@shiro.hasPermission>
                    <ul class="treeview-menu">
                        <@shiro.hasPermission name="department:list">
                            <li name="department"><a href="/department/list.do"><i class="fa fa-circle-o"></i> 部门管理</a></li>
                        </@shiro.hasPermission>
                        <@shiro.hasPermission name="employee:list">
                            <li name="employee"><a href="/employee/list.do"><i class="fa fa-circle-o"></i> 员工管理</a></li>
                        </@shiro.hasPermission>
                        <@shiro.hasPermission name="permission:list">
                            <li name="permission"><a href="/permission/list.do"><i class="fa fa-circle-o"></i> 权限管理</a></li>
                        </@shiro.hasPermission>
                        <@shiro.hasPermission name="role:list">
                            <li name="role"><a href="/role/list.do"><i class="fa fa-circle-o"></i> 角色管理</a></li>
                        </@shiro.hasPermission>
                        <li name="classinfo"><a href="/classinfo/list.do"><i class="fa fa-circle-o"></i> 班级管理</a></li>
                    </ul>
    

    四、集成 EhCache

    在请求中一旦进行权限的控制都去到 Realm 中的 doGetAuthorizationInfo 方法进行授权,我们授权信息应该要从数据库中查询的。 如果每次授权都要去查询数据库就太频繁了,性能不好,所以需要用到缓存

    依赖

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.6.8</version>
    </dependency>
    

    配置缓存管理器

    shiro.xml

         <!-- 安全管理器 分发调度-->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!-- 配置数据源 -->
            <property name="realm" ref="crmRealm"/>
            <!-- 注册缓存管理器 -->
            <property name="cacheManager" ref="cacheManager"/>
        </bean>
    
    
        <!-- 缓存管理器 权限 角色 shiro有关的数据会被缓存 (清除缓存:注销,清除自己的   关闭服务器,清除所有)-->
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <!-- 设置配置文件 -->
            <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
        </bean>
    

    添加 ehcache 配置文件

    shiro-ehcache.xml

    <ehcache>
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    
    • maxElementsInMemory: 缓存最大个数。
    • eternal:对象是否永久有效,一但设置了,timeout 将不起作用。
    • timeToIdleSeconds: 设置对象在失效前的允许闲置时间(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。
    • timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当 eternal=false 对象不是永久有效时使用,默认是 0.,也就是对象存活时间无穷大。
    • memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为 FIFO(先进先出)或是 LFU(较少使用)。
    • clearOnFlush:内存数量最大时是否清除。
    • overowToDisk:当内存中对象数量达到 maxElementsInMemory 时,Ehcache 将会对象写到磁盘中。

    五、用户账号状态

    相关文章

      网友评论

          本文标题:CRM项目03-shiro02

          本文链接:https://www.haomeiwen.com/subject/fdavohtx.html