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<Role> roles = new HashSet<>();

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

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

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

private List<Permission> permissions = new ArrayList<>();

 

2.角色中添加权限展示

  1. <th data-options="field:'permissions',width:100,

formatter:formatPerms">权限</th>

  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

<!-- 一个弹出框,里面要装一个form表单 --><div id="editDlg" class="easyui-dialog" title="功能操作"

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

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

    <form id="editForm" method="post">

        <!-- 修改的话,这个id是必需的 -->

        <input id="roleId" name="id" type="hidden"/>

        <table cellpadding="5">

            <tr>

                <td>

                    名称:<input class="easyui-validatebox" type="" name="name"

                              data-options="required:true"></input>

                    编码:<input class="easyui-validatebox" type="text" name="sn"

                              data-options="required:true"></input>

                </td>

            </tr>

        </table>

        <div class="easyui-layout" style="width:100%;height:400px;">

            <div data-options="region:'west'" style="width:50%;">

                <table id="rolePermissionGrid">

                    <thead>

                        <tr>

                            <th data-options="field:'name',width:100">名称</th>

                            <th data-options="field:'sn',width:100">编码</th>

                            <th data-options="field:'url',width:100">资源路径</th>

                        </tr>

                    </thead>

                </table>

            </div>

            <div data-options="region:'center'">

                <table id="allPermissionGrid">

                    <thead>

                    <tr>

                        <th data-options="field:'name',width:100">名称</th>

                        <th data-options="field:'sn',width:100">编码</th>

                        <th data-options="field:'url',width:100">资源路径</th>

                    </tr>

                    </thead>

                </table>

            </div>

        </div>

    </form>

    <div style="text-align:center;padding:5px">

        <!-- 如果这里我改成提交按钮,它就会提交整个页面 -->

        <a href="javascript:void(0)" class="easyui-linkbutton" data-method="save">提交</a>

        <a href="javascript:void(0)" class="easyui-linkbutton" οnclick="$('#editDlg').dialog('close')">关闭</a>

</div>

</div>

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<allRows.length;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<String,String> createFilterChainDefinitionMap(){
        Map<String, String> 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<Permission> 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<String> findSnByEmp(Long employeeId);

 

B.IPermissionService

Set<String> findSnByEmp(Long employeeId);

 

C.PermissionServiceImpl

@Override

public Set<String> 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<String> 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中引入自定义拦截器

  <!-- 5.shiro的真实过滤器(注:这个名称必需和web.xml的代表过滤器【DelegatingFilterProxy】名称一样) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <!-- 登录的url,如果没有登录,你访问的路径会跳到这个页面 -->
    <property name="loginUrl" value="/login"/>
    <!-- 登录成功的url,如果登录成功,会跳转到这个页面 -->
    <property name="successUrl" value="/main"/>
    <!-- 没有权限时跳转到这个位置 -->
    <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
    <!--
        配置哪些资源被保护,哪些资源需要权限
        anon:不需要登录也可以访问相应的权限
        authc:需要权限才能访问
          /** :所有文件及其子文件
    -->
   <!-- <property name="filterChainDefinitions">
        <value>
            /s/login.jsp = anon
            /** = authc
        </value>
    </property>-->
    <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>

    <!-- 引用自定义的权限过滤器 -->
    <property name="filters">
        <map>
            <entry key="yxbPerms" value-ref="itSourcePermissionsFilter"></entry>
        </map>
    </property>
</bean>

<!-- 配置自定义权限处理的过滤器-->
<bean id="itSourcePermissionsFilter" class="cn.itsource.wy.shiro.realm.ItSourcePermissionsAuthorizationFilter"></bean>

<!-- 这个bean是帮助咱们获取相应的值:它会到一个工厂bean中通过对应的方法拿到相应的值 -->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
      factory-method="createFilterChainDefinitionMap"></bean>
<!-- 配置可以创建 -->
<bean id="filterChainDefinitionMapBuilder" class="cn.itsource.wy.shiro.realm.FilterChainDefinitionMapBuilder"></bean>

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

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

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createFilterChainDefinitionMap(){
        Map<String, String> 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<Permission> 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;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值