Shiro_权限

目录

 

1.角色crud

1.前提:

2.角色中添加权限展示

3.页面的完善form与grid

4.为角色添加权限/删除权限

5.解决只增不减的问题

2.权限

1.获取所有权限进行判断

2.完成权限的判断

1.拿到登录用户----->主体

2.UserContext拿到当前登录用户

3.LoginController:登陆成功把用户放入Session作用域

4.根据id拿到权限

5.JpaRealm:进行权限的判断

3.权限判断Ajax请求----->自定义拦截器

1.自定义权限过滤器

2.shiro中引入自定义拦截器

3.把路径与资源放到拦截器中去


1.角色crud

1.前提:

使用代码生成器,生成Role,Permission并设置多对多的关系

使用代码生成器,生成Role,Permission并设置多对多的关系

    //Employee@ManyToMany@JoinTable(name = "employee_role",

        joinColumns = @JoinColumn(name="employee_id"),

        inverseJoinColumns = @JoinColumn(name="role_id"))

private Set roles = new HashSet<>();

//Role@ManyToMany@JoinTable(name="role_permission",

        joinColumns = @JoinColumn(name="role_id"),

        inverseJoinColumns = @JoinColumn(name="permission_id"))

private List permissions = new ArrayList<>();

 

2.角色中添加权限展示

  1. formatter:formatPerms">权限

    1. //返回权限的展示方法

        //v:当前数据    r:当前行数据   i:行索引

               function formatPerms(v,r,i){

         var permsName = "";

        for(let o of v){

            permsName += o.name +" ";

         }

         return permsName;}

     

    3.页面的完善form与grid

        左(当前权限)右(所有权限)都有一个grid

         data-options="iconCls:'icon-save',closed:true,modal:true"

         style="padding:10px;width: 850px;">

        

            

            

            

                

                    

                

            

                        名称:

                                  data-options="required:true">

                        编码:

                                  data-options="required:true">

                    

            

                

                    

                        

                            

                                

                                

                                

                            

                        

                    

    名称 编码 资源路径

                

                

                    

                        

                        

                            

                            

                            

                        

                        

                    

    名称 编码 资源路径

                

            

        

        

            

            提交

            关闭

    4.为角色添加权限/删除权限

       ...

    var rolePermissionGrid = $("#rolePermissionGrid");var allPermissionGrid = $("#allPermissionGrid");

    ...

     itsource = {

            add(){

             ...

                //清空grid中的数据

                //loadData:加载本地数据,旧的行将被移除

                rolePermissionGrid.datagrid("loadData",[]);

             ...

            },

            edit(){

               ...

                if(row){

                   ...

                    //拷备对应的权限数组

                    var copyPerms = [...row.permissions];

                    //解决Grid加显问题

                    rolePermissionGrid.datagrid("loadData",copyPerms);

                }else {

                  ...

            },

            //通过javascript进行保存

            save(){

               ...

                editForm.form('submit', {

                    //form提交的路径

                    url:url,

                    //提交之前你要做什么事件

                    onSubmit: function(param){

                        //添加一些提交的额外参数

                        //1.拿到grid中所有的值

                        var allRows = rolePermissionGrid.datagrid("getRows");

                        //2.循环把值放进去

                        for(var i=0;i

                            var row = allRows[i];

                            param[`permissions[${i}].id`] = row.id;

                        }

                        return $(this).form('validate');

                    },

                   ...

                });

            },

           ...

            //添加一个权限

            addPerms(index, row){

                //先拿到角色的所有权限

                var allRows = rolePermissionGrid.datagrid("getRows");

                //遍历进行比较

                for(let o of allRows){

                    //如果两个权限相等,就什么都不做了

                    if(o.id == row.id){

                        $.messager.show({

                            title:'注意事项',

                            msg:'这个权限已经存在,无需再进行添加!',

                            timeout:2000,

                            showType:'slide'

                        });

                        return;

                    }

                }

                rolePermissionGrid.datagrid("appendRow",row);

            },

            //删除对应权限

            delPerms(index,row){

                rolePermissionGrid.datagrid("deleteRow",index);

            }

        };

     

        //创建当前角色对应的权限(grid控件)

        rolePermissionGrid.datagrid({

            fit:true,

            fitColumns:true,

            singleSelect:true,

            border:false,

            onDblClickRow:itsource.delPerms

        })

     

        //创建拿到所有的权限的grid控件

        allPermissionGrid.datagrid({

            fit:true,

            url:'/permission/page',

            fitColumns:true,

            singleSelect:true,

            pagination:true,

            border:false,

            onDblClickRow:itsource.addPerms

        })

    5.解决只增不减的问题

        修改的时候只能添加不能减少

    @ModelAttribute("editRole")public Role beforeEdit(Long id,String cmd){

        //修改的时候才查询(只要有id会就进行一次查询,这是不对的)

        if(id!=null && "update".equals(cmd)) {

            Role role = roleService.findOne(id);

            //把要传过来的关联对象都清空,就可以解决n-to-n的问题

            role.getPermissions().clear();

            return role;

        }

        return null;

    }

    2.权限

    1.获取所有权限进行判断

     //静态资源放行
    public class FilterChainDefinitionMapBuilder {

        @Autowired
        private IPermissionService permissionService;

        public Map createFilterChainDefinitionMap(){
            Map filterChainDefinitionMap = new LinkedHashMap();
            //注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
            filterChainDefinitionMap.put("/login","anon");
            filterChainDefinitionMap.put("*.js","anon");
            filterChainDefinitionMap.put("*.css","anon");
            filterChainDefinitionMap.put("/css/**","anon");
            filterChainDefinitionMap.put("/js/**","anon");
            filterChainDefinitionMap.put("/easyui/**","anon");
            filterChainDefinitionMap.put("/images/**","anon");
            filterChainDefinitionMap.put("/media/**","anon");
            filterChainDefinitionMap.put("/kaptcha","anon");
            filterChainDefinitionMap.put("/wechat/**","anon");
            filterChainDefinitionMap.put("/wechat/callback","anon");
            filterChainDefinitionMap.put("/logout","logout"); //不登录也可以访问


            //1.拿到数据,放入map中
            List premission =permissionService.findAll();
            //2.便利权限,拿到权限和资源
            for (Permission permission:premission){
                String url = permission.getUrl();//资源
                String sn = permission.getSn();//权限
                //把路径和资源放到拦截其中
                filterChainDefinitionMap.put(url,"yxbPerms["+sn+"]" );
            }
            filterChainDefinitionMap.put("/**","authc");
            return  filterChainDefinitionMap;
        }
    }

     

    2.完成权限的判断

    1.拿到登录用户----->主体

          //AuthenticationInfo:认证; 身份验证; 证明
    //登录的时候就会调用这个方法来做验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //身份认证(用户名)
        // 1.拿到用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        String username = usernamePasswordToken.getUsername();

        // 2.根据用户名到数据库拿到用户
        Employee loginUser = employeeService.findByUsername(username);
        if(loginUser==null){
            return null;
            //该用户名不存在
        }
        //从数据库中拿到密码
        Object credentials = loginUser.getPassword();

        //加盐的数据   主体
        ByteSource byteSource = ByteSource.Util.bytes("itsource");
        return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
    }

     

    修改代码:

    <shiro:user>
        欢迎[ <shiro:principal property="username"  />]登录,
        <a href="${pageContext.request.contextPath}/logout">退出a>
    shiro:user>

     

    2.UserContext拿到当前登录用户

          public class UserContext {

        private static final String USER_IN_SESSION = "loginUser";
        /**
         * 把当前登录用户放入Session
         * @param loginUser
         */
        public static void setUser(Employee loginUser) {
            Subject subject = SecurityUtils.getSubject();
            subject.getSession().setAttribute(USER_IN_SESSION, loginUser);
        }
        /**
         * 从Session中获取User
         */
        public static Employee getUser() {
            Subject subject = SecurityUtils.getSubject();
            Employee curentUser = (Employee) subject.getSession().getAttribute(USER_IN_SESSION);
            return curentUser;
        }
    }

     

    3.LoginController:登陆成功把用户放入Session作用域

        @Controller
    public class LoginController {
        //用于完成跳转
        @RequestMapping(value="/login",method = RequestMethod.GET)
        public String index(){
            return "login";
        }


        //用于完成登录
        @RequestMapping(value="/login",method = RequestMethod.POST)
        @ResponseBody
        public JsonResult login(String username, String password, String code, HttpServletRequest req, HttpServletResponse resp){

            /*验证码验证*/
            /**
             * 获取当前的 Subject. 调用 SecurityUtils.getSubject();
             * 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
             * 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
             * 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
             */

            String yzm = req.getParameter("code");

            // 验证码未输入
            if (yzm == null || "".equals(yzm)) {
                // 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
                // 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码为空
                return new JsonResult(false,"请输入验证码");
            }
            // 获取SESSION中的验证码
            // Kaptcha在生成验证码时会将验证码放入SESSION中
            // 默认KEY为KAPTCHA_SESSION_KEY, 可以在Web.xml中配置
            String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute("KAPTCHA_SESSION_KEY");
            // 比较登录输入的验证码和SESSION保存的验证码是否一致
            if (!yzm.equals(sessionCaptcha)) {
                // 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
                // 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码错误
                return new JsonResult(false,"验证码错误");
            }

            //1.拿到访问的主体(当前登录用户)
            Subject subject = SecurityUtils.getSubject();
            //2.判断这个用户是否已经登录(通过验证)
            if(!subject.isAuthenticated()){
                //3.如果没有验证,就要完成登录
                UsernamePasswordToken token = new UsernamePasswordToken(username,password);
                try{
                    //4.根据toke完成登录功能
                    subject.login(token);
                }catch (UnknownAccountException e){
                    System.out.println("用户名不存在!!");
                    e.printStackTrace();
                    return new JsonResult(false,"账号或者密码出错!");
                }catch (IncorrectCredentialsException e){
                    System.out.println("密码不存在!");
                    e.printStackTrace();
                    return new JsonResult(false,"账号或者密码出错!");
                }catch (AuthenticationException e){
                    System.out.println("登录出错!");
                    e.printStackTrace();
                    return new JsonResult(false,"程序发生未知错误!");
                }

            }
            
            //登陆成功后,把当前登陆用户放到session中
            //1.拿到当前用户
            Employee loginUser = (Employee) subject.getPrincipal();
            //2.当前登陆用户放到sessiom中
            UserContext.setUser(loginUser);
            return new JsonResult();
        }
        //登出方法
        @RequestMapping("/logout")
        public String logout(){
            //登出
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return "redirect:/login";
        }
    }

     

    4.根据id拿到权限

    A. PermissionRepository

    //根据当前登录用户拿到对应的权限

    @Query("select distinct p.sn from Employee e join e.roles r join r.permissions p where e.id = ?1")

    Set findSnByEmp(Long employeeId);

     

    B.IPermissionService

    Set findSnByEmp(Long employeeId);

     

    C.PermissionServiceImpl

    @Override

    public Set findSnByEmp(Long employeeId) {

           return permissionRepository.findSnByEmp(employeeId);}

    5.JpaRealm:进行权限的判断

       public class JpaRealm extends AuthorizingRealm {

     

        @Autowired

        private IEmployeeService employeeService;

        @Autowired

        private IPermissionService permissionService;

     

        //AuthorizationInfo:授权(是否有权限进入操作)

        // 我们只需要把相应的权限交给Shiro,它就会自动比对

        @Override

        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

            //拿到主体信息(指的就是当前登录用户)

            Employee loginUser = UserContext.getUser();

            //获取权限资源(这里假设已经根据用户名到数据库中获取到了)

            Set permissions = permissionService.findSnByEmp(loginUser.getId());

            //permissions.add("employee:index");

            //permissions.add("role:index");

            //permissions.add("employee:*");

     

            //拿到授权对象,并且所有权限交给它

            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

            simpleAuthorizationInfo.setStringPermissions(permissions);

            //返回授权对象

            return simpleAuthorizationInfo;

        }

    }

    3.权限判断Ajax请求----->自定义拦截器

      注意:请求的分类

    1. 跳转页面xxx/index
    2. Ajax请求返回 {“success”:false,”message”:”没有权限”}

    重点:怎么判断Ajax请求?

    判断请求头里面是否有X-Requested-With

     

    1.自定义权限过滤器


    public class ItSourcePermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            Subject subject = this.getSubject(request, response);
            if (subject.getPrincipal() == null) {
                //没有登录成功后的操作
                this.saveRequestAndRedirectToLogin(request, response);
            } else {
                //登录成功后没有权限的操作

                //1.转成http的请求与响应操作
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                HttpServletResponse httpResponse = (HttpServletResponse) response;

                //2.根据请求判断是什么请求
                String xRequestedWith = httpRequest.getHeader("X-Requested-With");
                if (xRequestedWith != null &&"XMLHttpRequest".equals(xRequestedWith)) {

                    //3.在这里就代表是ajax请求
                    //表示ajax请求 {"success":false,"message":"没有权限"} --javaweb
                    httpResponse.setContentType("text/json; charset=UTF-8");
                    httpResponse.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");


                }else {
                    String unauthorizedUrl = this.getUnauthorizedUrl();
                    if (StringUtils.hasText(unauthorizedUrl)) {
                        WebUtils.issueRedirect(request, response, unauthorizedUrl);
                    } else {
                        WebUtils.toHttp(response).sendError(401);
                    }
                }
            }
            return false;
        }}

    2.shiro中引入自定义拦截器

      

        
        
        
        
        
        
        
        
       
        

        
        
            
                yxbPerms" value-ref="itSourcePermissionsFilter">
            
        





          factory-method="createFilterChainDefinitionMap">

    3.把路径与资源放到拦截中去

       //静态资源放行
    public class FilterChainDefinitionMapBuilder {

        @Autowired
        private IPermissionService permissionService;

        public Map createFilterChainDefinitionMap(){
            Map filterChainDefinitionMap = new LinkedHashMap();
            //注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
            filterChainDefinitionMap.put("/login","anon");
            filterChainDefinitionMap.put("*.js","anon");
            filterChainDefinitionMap.put("*.css","anon");
            filterChainDefinitionMap.put("/css/**","anon");
            filterChainDefinitionMap.put("/js/**","anon");
            filterChainDefinitionMap.put("/easyui/**","anon");
            filterChainDefinitionMap.put("/images/**","anon");
            filterChainDefinitionMap.put("/media/**","anon");
            filterChainDefinitionMap.put("/kaptcha","anon");
            filterChainDefinitionMap.put("/wechat/**","anon");
            filterChainDefinitionMap.put("/wechat/callback","anon");
            filterChainDefinitionMap.put("/logout","logout"); //不登录也可以访问

            //这个值之后从数据库中查询到【用户-角色-权限-资源】/wechat
            //http://bugtracker.itsource.cn/wechat/callback
            //filterChainDefinitionMap.put("/s/permission.jsp","perms[user:*]");
            //filterChainDefinitionMap.put("/s/employee.jsp","perms[employee:*]");
            //1.拿到数据,放入map中
            List premission =permissionService.findAll();
            //2.便利权限,拿到权限和资源
            for (Permission permission:premission){
                String url = permission.getUrl();//资源
                String sn = permission.getSn();//权限
                //把路径和资源放到拦截其中
                filterChainDefinitionMap.put(url,"yxbPerms["+sn+"]" );
            }
            filterChainDefinitionMap.put("/**","authc");
            return  filterChainDefinitionMap;
        }
    }

     

你可能感兴趣的:(shiro)