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; }
五、源码点我
- SSH物流开发系统设计:权限认证实现
- SSH物流开发系统设计:搭建框架并实现登录
- SSH物流开发系统设计:业务受理逻辑实现
- SSH物流开发系统设计:业务人员的增删改查
- SSH开发的物流管理系统
- ssh开发物流管理系统源代码下载
- SSH物流开发系统设计:定区的相关操作和设计
- SSH物流开发系统设计:区域和分区的增删改查
- 用户权限设计 ASP.NET系统用户权限设计与实现、用户认证管理设计方案、通用数据权限管理系统设计
- django认证系统实现自定义权限管理
- 第三方物流管理系统的设计与实现
- 通用权限系统设计实现
- 物流管理系统开发前奏
- 权限管理 (一) 设计思路分析和实现授权、认证
- ssh+easyui权限系统
- bombing:4证书认证系统设计与实现-实现上
- bombing:5证书认证系统设计与实现-实现下
- 系统权限设计与实现入门
- Afnetworking访问遇到3840_错误
- 数据库表的连接用法详解(join)
- 038-Java-29
- 【算法题】分苹果
- javascript
- SSH物流开发系统设计:权限认证实现
- ML与iOS的联姻
- SnakeDown 2017 Online Pre-Elimination Round A D 结题报告:三分
- ERRNO错误
- JAVA中日期计算
- 【Python学习系列九】Python机器学习库scikit-learn实现SVM案例
- Excel VBA高效办公应用-第一章-Excel VBA简介
- C#中的多线程
- XZ_iOS之实现点击某个按钮退出app