欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

shiro权限学习笔记

程序员文章站 2022-05-31 17:34:21
...

一、登录

1、准备加密工具类

(1)静态方法

(2)全局变量

目的:在自定义realm时可以用,而且不被修改

package cn.itsource.aisell.common;
import org.apache.shiro.crypto.hash.SimpleHash;
public class MD5Utils {
    public static final String SALT = "柠檬";
    public static final Integer HASHITERSTIONS = 10;
    public static String getPassword(String password){
        SimpleHash hash = new SimpleHash("md5", password, SALT, HASHITERSTIONS);
        return hash.toHex();
    }
}
2、自定义realm连接数据库验证

(1)注意:加盐需要转成ByteSource对象

ByteSource source = ByteSource.Util.bytes(MD5Utils.SALT);

(2)applicationContext-shiro.xml

<!-- 自定义的realm -->
    <bean id="jdbcRealm" class="cn.itsource.aisell.shiro.JpaRealm">
        <!--设置匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"/>
                <property name="hashIterations" value="10"></property>
            </bean>
        </property>
    </bean>
3、添加员工时给密码加密

(1)业务功能写在service层

覆写save方法,加上加密算法

 @Override
    public void save(Employee employee){
        if (employee.getId()==null){
            String password = MD5Utils.getPassword(employee.getPassword());
            employee.setPassword(password);
        }
        employeeRepository.save(employee);
    }
4、登录的两个controller

(1)跳转到登录界面controller

 //与-shiro.xml中的没有登录时要跳转的界面对应  get请求
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public String login(){
        //跳转到登录界面
        return "/login";
    }

(2)真正做登录验证的controller

//真正的处理登录请求的界面  post请求
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public SuccessMsg login(String username, String password){
        Subject current = SecurityUtils.getSubject();
        if(!current.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            try {
                current.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                return new SuccessMsg(false,"账号不存在!");
            }catch (IncorrectCredentialsException e){
                e.printStackTrace();
                return new SuccessMsg(false,"账号或密码错误!");
            }catch (AuthenticationException e){
                System.out.println("系统正在维护。。。");
                return new SuccessMsg(false,"网络异常!请重试!");
            }
        }
        return   new SuccessMsg();
    }
5、前台登录

(1)登录界面js

function submitForm() {
            $("#loginForm").form('submit',{
                url: "/login",
                onSubmit: function(){
                    var isValid = $(this).form('validate');
                    if (!isValid){
                        $.messager.progress('close');	// 如果表单是无效的则隐藏进度条
                    }
                    return isValid;	// 返回false终止表单提交
                },
                success: function(data){
                    var result =JSON.parse(data);
                    if (result.success){
                        window.location.href ="/index";
                    }else {
                        $.messager.alert('我的消息',result.msg,'error');	// 如果提交成功则隐藏进度条
                    }
                }
            });
        };

(2)按Enter登录

//按键登录
        $(window).keydown(function(event){
            if (event.keyCode==13){
                submitForm();
            };
        });
6、登录过期界面产生嵌套问题

(1)原因:
登录过期后,会跳转到登录界面,但是使用的是嵌套布局,所以需要判断当前登录界面是够是最顶层的窗口

 //解决嵌套问题
        //如果不是*父类,就跳转到*父类
        if(top!=window){
            top.location.href = window.location.href;
        };
7、静态资源放行

(1)shiro会把所有东西过滤掉

//设置静态资源不用过滤
        filterChainDefinitionMap.put("*.js", "anon");
        filterChainDefinitionMap.put("*.css", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/layui/**", "anon");
        filterChainDefinitionMap.put("/easyui/**", "anon");
        filterChainDefinitionMap.put("/s/**", "anon");
8、注销
 //注销登录
    @RequestMapping("/logout")
    public String logout(){
        Subject current = SecurityUtils.getSubject();
        current.logout();
        return "redirect:/login";
    }

二、角色与权限

1.员工-角色-权限之间的关系

(1)员工与角色————单向多对多

@ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "employee_role",
            joinColumns = @JoinColumn(name = "employee_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id")
    )

(2)角色与全向————单向多对多

private String sn;
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "role_permission",
            joinColumns = @JoinColumn(name = "role_id"),
            inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
2、角色中权限的展示

(1)显示全向名字

与显示图片类似

···
 <th data-options="field:'permissionList',formatter:permFormat,width:100" align="center">权限</th>
···

js中

···
function permFormat(v){
//显示权限名字
    var param = "";
    for(let o of v){
        perm +=o.name+" ";
    };
    return perm;
}
···
3、给角色添加权限

(1)问题1:怎么将权限全部显示出来

在dialog中用datagrid表格显示,将Permission表中所有数据加载到表格

<%--点击增加修改按钮后的弹出框--%>
<div id="editDig" class="easyui-dialog" title="role操作" style="padding:10px;width: 850px;"
     data-options="iconCls:'icon-save',resizable:true,modal:true,closed:true">
    <div style="padding:10px 60px 20px 40px">
    <form id="ff" class="easyui-form" method="post">
        <input id="id" type="hidden" name="id" />
        <table cellpadding="5">
            <tr>
                <td style="text-align: right">名字:</td>
                <td>
                    <input class="easyui-validatebox" type="text" name="name"
                           data-options="required:true"></input>
                </td>
                <td style="text-align: right">编码:</td>
                <td>
                    <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'" style="width:50%">
                <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>

(2)问题2:怎么从有所有权限的表中,选取权限给角色

//双击右边,左边添加数据
        addPerms(index,row){
            var allrows = rolePermissionGrid.datagrid('getRows');
            for (let o of allrows){
                if(o.id == row.id){
                    $.messager.show({
                        title:'我的消息',
                        msg:'已存在该权限!',
                        timeout:1000,
                        showType:'fade',
                        style:{
                            right:'',
                            top:document.body.scrollTop+document.documentElement.scrollTop,
                            bottom:''
                        }
                    });
                    return;
                };
            }
            rolePermissionGrid.datagrid('appendRow',row);
        },
        //双击移除行
        delperm(index){
            rolePermissionGrid.datagrid('deleteRow',index);
        }
    };
 rolePermissionGrid.datagrid({
        fit:true,//只有充满才能显示数据
        fitColumns:true,
        singleSelect:true,
        pagination:true,
        border:false,
        onDblClickRow:itsource.delperm
    })
    allPermissionGrid.datagrid({
        fit:true,//只有充满才能显示数据
        url:'/permission/pagelist',
        fitColumns:true,
        singleSelect:true,
        pagination:true,
        border:false,
        onDblClickRow:itsource.addPerms
    })

(3)问题3:怎么提交数据
form表单提交额外数据

onSubmit: function(param){
                    var roleparms = rolePermissionGrid.datagrid('getRows');
                    for(var i=0;i<roleparms.length;i++){
                        // 传递额外参数  传递到后台的参数名字:permissionList[i].id = id
                        param[`permissionList[${i}].id`]= roleparms[i].id;
                    }
                    //validate none 做表单字段验证,当所有字段都有效的时候返回true。该方法使用validatebox(验证框)插件。
                    var isValid = $(this).form('validate');
                    if (!isValid){
                        $.messager.progress('close'); // 如果表单是无效的则隐藏进度条
                    }
                    return isValid; // 返回false终止表单提交
                },
4、修改权限

(1)问题:当修改权限,但是没提交就关闭修改框后,再次点击修改按钮,不能正确实现原有权限

解决方案:将原数据拷贝一份

 //表单加载已选择的数据
                editform.form('load',select);
                //将选中的数据备份一份, 免得被污染
                var copyPerms = [...select.permissionList];
                //权限表格加载本地数据
                rolePermissionGrid.datagrid('loadData',copyPerms);
5、修改提交

(1)问题1:当修改后的权限数量比原来的少时,会保存失败
原因:解决数据丢失的方法没有写完整

解决方案:

//解决修改时数据丢失问题,会给另一个形参加上此注解、且名字一样的,一个查询好的对象。
    @ModelAttribute("editRole")
    public Role lossData(Long id,String cmd){
        if (id!=null&&"updata".equals(cmd)){
            Role role = roleService.findOne(id);
            //设置关联对象清空,解决修改的时候出错。(修改后的权限数量比原来的多)
            role.getPermissionList().clear();
            return role;
        }
        return  null;
    }