SSH物流开发系统设计:权限认证实现

来源:互联网 发布:中医元阳灸网络课程 编辑:程序博客网 时间:2024/05/17 04:10

一、权限认证框架Shiro

系统提供了很多功能,并不是所有的用户登录系统都可以操作这些功能。我们需要对用户的访问进行控制。
认证:系统提供的用于识别用户身份的功能(通常是登录功能)—–让系统知道你是谁??
授权:系统提供的赋予用户访问某个功能的能力—–让系统知道你能做什么??
这里使用Shiro来进行权限认证,关于Shiro的介绍和使用请参考这里。点我

二、将shiro应用到bos项目

第一步:导入shiro-all.jar
第二步:在web.xml中配置一个spring用于整合shiro的过滤器

  <filter>    <filter-name>shiroFilter</filter-name>    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  </filter>  <filter-mapping>    <filter-name>shiroFilter</filter-name>    <url-pattern>/*</url-pattern>  </filter-mapping>

第三步:在spring配置文件中配置一个bean,id必须和上面的过滤器名称相同

<!-- 配置一个工厂bean,用于创建shiro框架用到过滤器 -->    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <!-- 注入安全管理器 -->        <property name="securityManager" ref="securityManager"></property>        <!-- 注入当前系统的登录页面 -->        <property name="loginUrl" value="/login.jsp"/>        <!-- 注入成功页面 -->        <property name="successUrl" value="/index.jsp"/>        <!-- 注入权限不足提示页面 -->        <property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"/>        <!-- 注入URL拦截规则 -->        <property name="filterChainDefinitions">            <value>                /css/** = anon                /images/** = anon                /js/** = anon                /login.jsp* = anon                /validatecode.jsp* = anon                /userAction_login.action = anon                /page_base_staff.action = perms["staff"]                /* = authc            </value>        </property>    </bean>

第四步:在spring配置文件中注册安全管理器,为安全管理器注入realm

<!-- 注册自定义realm -->    <bean id="bosRealm" class="com.crm.bos.shiro.BOSRealm"></bean>    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">        <!-- 注入上面的realm -->        <property name="realm" ref="bosRealm"/>    </bean>

第五步:自定义一个BOSRealm

public class BOSRealm extends AuthorizingRealm {    @Resource    private IUserDao userDao;    /**     * 认证方法     */    protected AuthenticationInfo doGetAuthenticationInfo(            AuthenticationToken token) throws AuthenticationException {        System.out.println("认证方法。。。");        UsernamePasswordToken upToken = (UsernamePasswordToken) token;        String username = upToken.getUsername();// 从令牌中获得用户名        User user = userDao.findUserByUsername(username);        if (user == null) {            // 用户名不存在            return null;        } else {            // 用户名存在            String password = user.getPassword();// 获得数据库中存储的密码            // 创建简单认证信息对象            /***             * 参数一:签名,程序可以在任意位置获取当前放入的对象             * 参数二:从数据库中查询出的密码             * 参数三:当前realm的名称             */            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,                    password, this.getClass().getSimpleName());            return info;//返回给安全管理器,由安全管理器负责比对数据库中查询出的密码和页面提交的密码        }    }    /**     * 授权方法     */    protected AuthorizationInfo doGetAuthorizationInfo(            PrincipalCollection principals) {        return null;    }}

第六步:完善UserAction的login方法

public String login(){        //生成的验证码        String key = (String) ServletActionContext.getRequest().getSession().getAttribute("key");        //判断用户输入的验证码是否正确        if(StringUtils.isNotBlank(checkcode) && checkcode.equals(key)){            //验证码正确            //获得当前用户对象            Subject subject = SecurityUtils.getSubject();//状态为“未认证”            String password = model.getPassword();            password = MD5Utils.md5(password);            //构造一个用户名密码令牌            AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), password);            try{                subject.login(token);            }catch (UnknownAccountException e) {                e.printStackTrace();                //设置错误信息                this.addActionError(this.getText("usernamenotfound"));                return "login";            }catch (Exception e) {                e.printStackTrace();                //设置错误信息                this.addActionError(this.getText("loginError"));                return "login";            }            //获取认证信息对象中存储的User对象            User user = (User) subject.getPrincipal();            ServletActionContext.getRequest().getSession().setAttribute("loginUser", user);            return "home";        }else{            //验证码错误,设置错误提示信息,跳转到登录页面            this.addActionError(this.getText("validateCodeError"));            return "login";        }    }

第七步:在自定义Realm中编写授权方法

    /**     * 授权方法     */    protected AuthorizationInfo doGetAuthorizationInfo(            PrincipalCollection principals) {        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();        //根据当前登录用户查询其对应的权限,授权        User user = (User) principals.getPrimaryPrincipal();        List<Function> list = null;        if(user.getUsername().equals("admin")){            //当前用户是超级管理员,查询所有权限,为用户授权            list = functionDao.findAll();        }else{            //普通用户,根据用户id查询对应的权限            list = functionDao.findListByUserid(user.getId());        }        for (Function function : list) {            info.addStringPermission(function.getCode());        }        return info;    }

三、shiro提供的权限控制方式

1、URL拦截权限控制

<!-- 注入URL拦截规则 -->        <property name="filterChainDefinitions">            <value>                /css/** = anon                /images/** = anon                /js/** = anon                /login.jsp* = anon                /validatecode.jsp* = anon                /userAction_login.action = anon                /page_base_staff.action = perms["staff"]                /* = authc            </value>        </property>

2、方法注解权限控制
第一步:在spring配置文件中开启shiro的注解支持

<!-- 开启shiro注解 -->    <!-- 自动代理 -->    <bean id="defaultAdvisorAutoProxyCreator"         class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">        <!-- 强制使用cglib为Action创建代理对象 -->        <property name="proxyTargetClass" value="true"></property>    </bean>    <!-- 切面类 -->    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>

第二步:在Action的方法上使用shiro的注解描述执行当前方法需要具有的权限。

/**     * 批量删除功能(逻辑删除)     * @return     */    @RequiresPermissions(value="staff")//执行当前方法需要具有staff权限    @RequiresRoles(value="abc")    public String delete(){        staffService.deleteBatch(ids);        return "list";    }

第三步:修改BaseAction的构造方法,判断是否为代理对象。

public BaseAction() {        ParameterizedType genericSuperclass = null;        if(this.getClass().getGenericSuperclass() instanceof ParameterizedType){            genericSuperclass = (ParameterizedType) this            .getClass().getGenericSuperclass();        }else{//当前为Action创建了代理            genericSuperclass = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();        }

第四步:在struts.xml中配置全局异常捕获,统一跳转到权限不足的页面

<!-- 全局异常捕获 -->        <global-exception-mappings>            <exception-mapping result="unauthorizedUrl"                exception="org.apache.shiro.authz.AuthorizationException"></exception-mapping>        </global-exception-mappings>

四、实现权限认证

初始化权限数据

执行sql脚本文件初始化权限数据:

INSERT INTO `auth_function` VALUES ('11', '基础档案', 'jichudangan', null, null, '1', '0', null);INSERT INTO `auth_function` VALUES ('112', '收派标准', 'standard', null, 'page_base_standard.action', '1', '1', '11');INSERT INTO `auth_function` VALUES ('113', '取派员设置', 'staff', null, 'page_base_staff.action', '1', '2', '11');INSERT INTO `auth_function` VALUES ('114', '区域设置', 'region', null, 'page_base_region.action', '1', '3', '11');INSERT INTO `auth_function` VALUES ('115', '管理分区', 'subarea', null, 'page_base_subarea.action', '1', '4', '11');INSERT INTO `auth_function` VALUES ('116', '管理定区/调度排班', 'decidedzone', null, 'page_base_decidedzone.action', '1', '5', '11');INSERT INTO `auth_function` VALUES ('12', '受理', 'shouli', null, null, '1', '1', null);INSERT INTO `auth_function` VALUES ('121', '业务受理', 'noticebill', null, 'page_qupai_noticebill_add.action', '1', '0', '12');INSERT INTO `auth_function` VALUES ('122', '工作单快速录入', 'quickworkordermanage', null, 'page_qupai_quickworkorder.action', '1', '1', '12');INSERT INTO `auth_function` VALUES ('124', '工作单导入', 'workordermanageimport', null, 'page_qupai_workorderimport.action', '1', '3', '12');INSERT INTO `auth_function` VALUES ('13', '调度', 'diaodu', null, null, '1', '2', null);INSERT INTO `auth_function` VALUES ('131', '查台转单', 'changestaff', null, null, '1', '0', '13');INSERT INTO `auth_function` VALUES ('132', '人工调度', 'personalassign', null, 'page_qupai_diaodu.action', '1', '1', '13');INSERT INTO `auth_function` VALUES ('14', '物流配送流程管理', 'zhongzhuan', null, null, '1', '3', null);INSERT INTO `auth_function` VALUES ('141', '启动配送流程', 'start', null, 'workOrderManageAction_list.action', '1', '0', '14');INSERT INTO `auth_function` VALUES ('142', '查看个人任务', 'personaltask', null, 'taskAction_findPersonalTask.action', '1', '1', '14');INSERT INTO `auth_function` VALUES ('143', '查看我的组任务', 'grouptask', null, 'taskAction_findGroupTask.action', '1', '2', '14');

权限分页查询

第一步:修改页面中datagrid的URL地址,访问FunctionAction的pageQuery的分页查询方法。

pageList: [10,20,30],            pagination : true,            url : '${pageContext.request.contextPath}/functionAction_pageQuery.action',            columns : [[              {                  field : 'id',                  title : '编号',                  width : 200              },              {                  field : 'name',                  title : '名称',                  width : 200              },                {                  field : 'description',                  title : '描述',                  width : 50              },                {                  field : 'generatemenu',                  title : '是否生成菜单',                  width : 200,                  formatter:function(data){                      if(data == '1'){                          return "是";                      }else{                          return "否";                      }                  }              },                {                  field : 'zindex',                  title : '优先级',                  width : 200              },                {                  field : 'page',                  title : '路径',                  width : 200              }            ]]

第二步:创建FunctionAction

@Controller@Scope("prototype")public class FunctionAction extends BaseAction<Function> {    public String pageQuery() {        pageBean.setCurrentPage(Integer.parseInt(model.getPage()));//赋给分页数据,否则这个page参数会赋给Function中的page属性        functionService.pageQuery(pageBean);        String[] excludes = new String[] { "function", "functions", "roles", "detachedCriteria"};        this.writePageBean2Json(pageBean, excludes);        return NONE;    }

第三步:配置struts.xml

<!-- 权限管理Action -->        <action name="functionAction_*" class="functionAction" method="{1}">            <result name="list">/WEB-INF/pages/admin/function.jsp</result>        </action>

权限添加功能

第一步:修改添加页面中的combobox的URL地址,查询所有的权限,展示到下拉框中

<form id="functionForm" method="post" action="${pageContext.request.contextPath }/functionAction_add.action">                <table class="table-edit" width="80%" align="center">                    <tr class="title">                        <td colspan="2">功能权限信息</td>                    </tr>                    <tr>                        <td width="200">权限关键字</td>                        <td>                            <input type="text" name="code" class="easyui-validatebox" data-options="required:true" />                                               </td>                    </tr>                    <tr>                        <td>名称</td>                        <td><input type="text" name="name" class="easyui-validatebox" data-options="required:true" /></td>                    </tr>                    <tr>                        <td>访问路径</td>                        <td><input type="text" name="page"  /></td>                    </tr>                    <tr>                        <td>是否生成菜单</td>                        <td>                            <select name="generatemenu" class="easyui-combobox">                                <option value="0">不生成</option>                                <option value="1">生成</option>                            </select>                        </td>                    </tr>                    <tr>                        <td>优先级</td>                        <td>                            <input type="text" name="zindex" class="easyui-numberbox" data-options="required:true" />                        </td>                    </tr>                    <tr>                        <td>父功能点</td>                        <td>                            <input name="function.id" class="easyui-combobox" data-options="valueField:'id',textField:'name',                            url:'${pageContext.request.contextPath }/functionAction_listajax.action'"/>                        </td>                    </tr>                    <tr>                        <td>描述</td>                        <td>                            <textarea name="description" rows="4" cols="60"></textarea>                        </td>                    </tr>                    </table>            </form>

第二步:在FunctionAction中提供listajax方法

public String listajax() throws IOException{        List<Function> list=functionService.findAll();        String[] excludes=new String[]{"function", "functions", "roles"};        this.writeList2Json(list, excludes);        return NONE;    }

第三步:为保存按钮绑定事件提交表单

<script type="text/javascript">    $(function(){        // 点击保存        $('#save').click(function(){            var v = $("#functionForm").form("validate");            if(v){                $("#functionForm").submit();            }        });    });

第四步:在Action中提供add方法,保存一个权限数据

 public String add(){        functionService.save(model);        return "list";    }

Service代码:

public void save(Function model) {        //需要判断传过来的pid是否是空串,若为空串则将其赋值为null,否则无法插入mysql中        Function parent = model.getFunction();        if(parent!=null && parent.getId()!=null&&parent.getId().equals("")){            model.setFunction(null);        }        functionDao.save(model);    }

角色管理(关联权限)

1、 添加角色(为角色授权)

1.1. 授权树的编写
显示授权树,根据auth_function 表查询,返回json数据

<script type="text/javascript">    $(function(){        // 授权树初始化        var setting = {            data : {                key : {                    title : "t"                },                simpleData : {                    enable : true                }            },            check : {                enable : true, //选中效果            }        };        $.ajax({            url : '${pageContext.request.contextPath}/functionAction_listajax.action',            type : 'POST',            dataType : 'json',            success : function(data) {                $.fn.zTree.init($("#functionTree"), setting, data);            },            error : function(msg) {                alert('树加载异常!');            }        });

服务器返回 ztree节点数据,应该包含 id、name、parentId !
在FunctionAction 添加treedata的方法

public String listajax() throws IOException{        List<Function> list=functionService.findAll();        String[] excludes=new String[]{"function", "functions", "roles"};        this.writeList2Json(list, excludes);        return NONE;    }

在Function 实体类 添加getParentId 方法

public String getpId() {//注意getpId中的“p”是小写,因为“I”是大写        if(function != null){            return function.getId();        }else{            return "0";        }    }

1.2. 提交添加角色表单

// 点击保存        $('#save').click(function(){            var v = $("#roleForm").form("validate");            if(v){                var treeObj = $.fn.zTree.getZTreeObj("functionTree");//获得页面中的ztree对象                var nodes = treeObj.getCheckedNodes(true);//在提交表单之前将选中的checkbox收集                var array = new Array();                for(var i=0;i<nodes.length;i++){                    var id = nodes[i].id;//权限id                    array.push(id);                }                var ids = array.join(",");//1,2,3,4,5                $("input[name=ids]").val(ids);                //alert();                $("#roleForm").submit();            }        });

编写服务器 RoleAction

@Controller@Scope("prototype")public class RoleAction extends BaseAction<Role>{    private String ids;//用来接受权限的id值    public String add(){        roleService.save(model,ids);        return "list";    }    public void setIds(String ids) {        this.ids = ids;    }

将DAO 注入 BaseService
将Service 注入BaseAction
添加角色业务代码service层:

public void save(Role model, String ids) {        roleDao.save(model);//变为持久对象        Group group=new GroupEntity(model.getName());        identityService.saveGroup(group);//将角色数据同步到actitviti的数据表中        String[] functionIds = ids.split(",");        for (String fid : functionIds) {            Function function=new Function();            function.setId(fid);//托管状态,建立关联时必须是持久类关联托管类            model.getFunctions().add(function);//角色和权限建立关联        }    }

配置跳转

<!-- 角色管理 -->        <action name="roleAction_*" class="roleAction" method="{1}">            <result name="list">/WEB-INF/pages/admin/role.jsp</result>        </action>

4.2. 角色列表查询
/WEB-INF/pages/admin/role.jsp 修改 datagrid 的url

pageList: [10,20,30],            pagination : true,            url : '${pageContext.request.contextPath}/roleAction_pageQuery.action',            columns : [[                {                    field : 'id',                    title : '编号',                    width : 230                },                {                    field : 'name',                    title : '名称',                    width : 200                },                 {                    field : 'description',                    title : '描述',                    width : 200                }             ]]

在服务器 RoleAction 添加方法

public String pageQuery(){        roleService.pageQuery(pageBean);        String[] excludes=new String[]{"functions","users"};        this.writePageBean2Json(pageBean, excludes);        return NONE;    }

用户管理(关联角色)

1、 添加用户
pojo类

package com.crm.bos.domain;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashSet;import java.util.Set;/** * User entity. @author MyEclipse Persistence Tools */public class User implements java.io.Serializable {    // Fields    private String id;    private String username;    private String password;    private Double salary;    private Date birthday;    private String gender;// 1表示男,2表示女    private String station;    private String telephone;    private String remark;    private Set<Noticebill> noticebills = new HashSet<Noticebill>();    private Set<Role> roles = new HashSet<Role>();    public String getFormateBirthday() {        if (birthday != null) {            return new SimpleDateFormat("yyyy-MM-dd").format(birthday);        } else {            return "未提交生日";        }    }    public String getRoleNames() {        String names = "";        if (roles != null && roles.size() > 0) {            for (Role role : roles) {                names += role.getName() + " ";            }        }        return names;    }    // Constructors    /** default constructor */    public User() {    }    /** minimal constructor */    public User(String id, String username, String password) {        this.id = id;        this.username = username;        this.password = password;    }    /** full constructor */    public User(String id, String username, String password, Double salary,            Date birthday, String gender, String station, String telephone,            String remark) {        this.id = id;        this.username = username;        this.password = password;        this.salary = salary;        this.birthday = birthday;        this.gender = gender;        this.station = station;        this.telephone = telephone;        this.remark = remark;    }    // Property accessors    public String getId() {        return this.id;    }    public void setId(String id) {        this.id = id;    }    public String getUsername() {        return this.username;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return this.password;    }    public void setPassword(String password) {        this.password = password;    }    public Double getSalary() {        return this.salary;    }    public void setSalary(Double salary) {        this.salary = salary;    }    public Date getBirthday() {        return this.birthday;    }    public void setBirthday(Date birthday) {        this.birthday = birthday;    }    public String getGender() {        return this.gender;    }    public void setGender(String gender) {        this.gender = gender;    }    public String getStation() {        return this.station;    }    public void setStation(String station) {        this.station = station;    }    public String getTelephone() {        return this.telephone;    }    public void setTelephone(String telephone) {        this.telephone = telephone;    }    public String getRemark() {        return this.remark;    }    public void setRemark(String remark) {        this.remark = remark;    }    public Set<Noticebill> getNoticebills() {        return noticebills;    }    public void setNoticebills(Set<Noticebill> noticebills) {        this.noticebills = noticebills;    }    public Set<Role> getRoles() {        return roles;    }    public void setRoles(Set<Role> roles) {        this.roles = roles;    }}

修改 /WEB-INF/pages/admin/userinfo.jsp

 <form id="form" method="post" action="${pageContext.request.contextPath}/userAction_add.action">           <table class="table-edit"  width="95%" align="center">                <tr class="title"><td colspan="4">基本信息</td></tr>                <tr><td>用户名:</td><td><input type="text" name="username" id="username" class="easyui-validatebox" required="true" /></td>                    <td>口令:</td><td><input type="password" name="password" id="password" class="easyui-validatebox" required="true" validType="minLength[5]" /></td></tr>                <tr class="title"><td colspan="4">其他信息</td></tr>                <tr><td>工资:</td><td><input type="text" name="salary" id="salary" class="easyui-numberbox" /></td>                    <td>生日:</td><td><input type="text" name="birthday" id="birthday" class="easyui-datebox" /></td></tr>                <tr><td>性别:</td><td>                    <select name="gender" id="gender" class="easyui-combobox" style="width: 150px;">                        <option value="1"></option>                        <option value="2"></option>                    </select>                </td>                    <td>单位:</td><td>                    <select name="station" id="station" class="easyui-combobox" style="width: 150px;">                        <option value="">请选择</option>                        <option value="总公司">总公司</option>                        <option value="分公司">分公司</option>                        <option value="厅点">厅点</option>                        <option value="基地运转中心">基地运转中心</option>                        <option value="营业所">营业所</option>                    </select>                </td></tr>                <tr>                    <td>联系电话</td>                    <td colspan="3">                        <input type="text" name="telephone" id="telephone" class="easyui-validatebox" required="true" />                    </td>                </tr>                <tr><td>备注:</td><td colspan="3"><textarea style="width:80%"></textarea></td></tr>                <tr><td>选择角色:</td>                    <td colspan="3" id="roleTD">                        <script type="text/javascript">                            $(function(){                                //发送ajax请求,获取所有的角色数据,返回json,在页面中动态构造到checkbox中                                var url = "${pageContext.request.contextPath}/roleAction_listajax.action";                                $.post(url,{},function(data){                                    //解析json数据,构造checkbox                                    for(var i=0;i<data.length;i++){                                        var id = data[i].id;                                        var name = data[i].name;                                        $("#roleTD").append('<input type="checkbox" value="'+id+'" name="roleIds">'+name);                                    }                                });                            });                        </script>                    </td>               </tr>           </table>       </form>

在RoleAction 添加 listajax 方法

public String listajax() throws IOException{        List<Role> list=roleService.findAll();        String[] excludes=new String[]{"functions","users"};        this.writeList2Json(list, excludes);        return NONE;    }

点击 保存按钮提交form表单

<script type="text/javascript">    $(function(){        $("body").css({visibility:"visible"});        $('#save').click(function(){            if($('#form').form("validate")){                $('#form').submit();            }        });    });</script>   

在 UserAction 添加 save 方法

private String[] roleIds;//接受页面提交过来的参数    public String add(){        userService.save(model,roleIds);        return "list";    }

service层代码:

public void save(User model, String[] roleIds) {        String password = model.getPassword();        password= MD5Utils.md5(password);//md5加密        model.setPassword(password);//设置加密后的密码        userDao.save(model);        for (String roleId : roleIds) {            Role role=roleDao.findById(roleId);            model.getRoles().add(role);            identityService.createMembership(actUser.getId(), role.getName());//关联角色信息        }    }

2、 用户列表查询

/WEB-INF/pages/admin/userlist.jsp
修改 datagrid 的 url

$(function(){        // 初始化 datagrid        // 创建grid        $('#grid').datagrid( {            iconCls : 'icon-forward',            fit : true,            border : false,            rownumbers : true,            striped : true,            toolbar : toolbar,            url : "${pageContext.request.contextPath}/userAction_pageQuery.action",            pageList: [3,5,10],            pagination : true,            idField : 'id',             frozenColumns : frozenColumns,            columns : columns,            onClickRow : onClickRow,            onDblClickRow : doDblClickRow        });        $("body").css({visibility:"visible"});    });

在服务器 UserAction 提供查询方法

public String pageQuery() throws IOException{        userService.pageQuery(pageBean);        String[] excludes=new String[]{"noticebills","roles"};        this.writePageBean2Json(pageBean, excludes);        return NONE;    }

五、源码点我

原创粉丝点击