您的位置:首页 > 其它

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

2017-06-08 17:41 417 查看

一、权限认证框架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;
}


五、源码点我

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: