[size=large]这个功能的思路是自己原创的,没看过其他例子,其实是在做之前网上搜了很多也没个可以参考的例子.主要技术是后台springsecurity做拦截判断,前台Extjs4做按钮动态渲染.如果你不了解springsecrity,那你可以先看下我的另一篇相关博客
[b]创建数据库表(liqiubase方式创建看上一篇)[/b] [/size]
[size=large]这张是权限表,也是菜单(按钮)表,每个菜单都有他自己需要访问链接的权限,当菜单类型为子菜单或按钮时还需要配置该按钮对应的js文件,对没错,每个按钮都有自己独立的js文件,在用户没有按钮权限的时候,前端是js都不需要加载的
[b]添加菜单初始数据[/b]
[/size]
[size=large]添加了一个父级菜单用户管理,两个子集菜单账号管理和角色管理,同时在账号管理下添加两个按钮新增和修改,需要注意的是新增的授权链接是account!save.action而修改的授权链接是account!save.action,account!find.action,因为修改需要先查再能改,当用户被授予新增和修改两个权限的时候,访问account!save.action,account!find.action也就不会受阻了
[b]创建角色表和角色权限关联表[/b]
[/size]
[size=large]角色和权限是多对多的关系,一个角色可以有多个权限,一个权限也可以属于多个角色,
配完初始数据以后前台展示大致是这样的(图片太大请在新窗口打开)[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3935/d7e898bc-1efd-34b3-ab94-8f6a3f974b0d.jpg[/img]
[size=large]
拥有角色管理的的用户可以添加删除角色,同时可以为没每个角色分配权限
[b]创建用户角色关联表[/b]
[/size]
[size=large]
这里给admin初始化了id为1的角色,也就是超级管理员,同样
拥有账号管理里的用户可以为账号添加角色
[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3944/6dfbee3e-fa7b-38f8-a9d9-4957e702118b.jpg[/img]
[size=large]到这里数据的配置基本讲完了,再展示下初始数据的liqiubase文件,大致就是那几张表,
最后我再给游客2分配了几个权限,新增一个角色为游客2的账号[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3948/d595180d-68b6-3a56-ab41-b109ec4ec99e.jpg[/img]
[img]http://dl2.iteye.com/upload/attachment/0110/4029/66a7093a-30df-3742-bd72-6053889ceabc.jpg[/img]
[img]http://dl2.iteye.com/upload/attachment/0110/3956/f9e4412e-70e6-386c-9b56-0adbf4eeedc5.jpg[/img]
[size=large]以上展示的只是数据配置的过程,通俗的讲就是想法,
现在想法已经出来了,权限也分配好了真正的权限控制核心代码就开始了,首先展示下我的hibernate entity
[/size]
[size=large]Account作为springsecrity的账户类必须实现UserDetails接口,实现的getAuthorities方法是告诉spring当前用户的角色,还有个自定义方法getSignPer用于获取当前用户的权限列表,Permission类有个方法getControlTypeByDg通过配置的jsUrl链接获取Extjs的组件类名,他们的用处后面就能看到,这里补充下我用的是Hibernate的注解方式,严格来讲项目中除了公共配置部分,其他包括Struts Spring等需要xml配置的我全用了注解,如果你对注解不是很了解用xml配置的形式也是可以的
用户通过springsecrity登录成功的时候,会把Account对象保存它的上下文,然后访问这个跳转到主页的action方法,action方法中从springsecrity上下文中获取到Account后通过getSignPer方法获取当前用户的权限(也就是菜单),下面就是用户cccq3y的菜单了
[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/4020/9f8e88f9-fd69-3a63-a9ec-34ac79a172d8.jpg[/img]
[size=large]当用户点击新增确认按钮时,浏览器会向后台提交一个表单,url为account!save.action,首先会被springsecrity的MyFilterInvocationSecurityMetadataSource拦截到[/size]
[size=large]返回的角色中应该有一个是游客2的id,然后再被MyAccessDecisionManager拦截到[/size]
[size=large]注释写个很清楚了,把当前用户所拥有的角色和MyFilterInvocationSecurityMetadataSource返回的访问当前链接需要哪些角色,如果包含说明当前用户能访问该链接了,再没抛AccessDeniedException错误的情况下验证都通过
最后再说下前端代码,首先主页的jsp要根据权限加载js文件
[/size]
[size=large]然后来感受下extjs4的强大之处了,首先我封装一个公共的权限工具条[/size]
[size=large]然后是grid基类[/size]
[size=large]把当前组件的xtype作为参数,到后台获取对应的父级菜单,再根据用户拥有的按钮权限获取这个用户在当前组件的按钮权限,那么问题来了,我是如果通过xtype定位到那条权限数据的呢,真相是我在配置账号管理这个权限的时候配置的jsUrl为user/account/searchGrid然后通过上面Permission类自定义的getControlTypeByDg方法转成xtype,再来看看我的grid子类就清楚了[/size]
[size=large]alias:widget.userAccountSearchGrid,这样就能和后台的权限做对应了,而且在点击菜单树的时候同样用到这个功能,我会根据当前被点击菜单的jsUrl属性转义成xtype,然后很方便的加载对应的组件,就这么简单.最后展示下我的js文件目录
[img]http://dl2.iteye.com/upload/attachment/0110/4032/fa508ba8-ae76-36ea-bbce-faa413ed1be7.jpg[/img]
写到这里差不多就结束了,写的有点乱,看不懂的可以加我qq探讨一下;
[/size]
[b]创建数据库表(liqiubase方式创建看上一篇)[/b] [/size]
<createTable tableName="permissions">
<column name="id" type="bigint">
<constraints nullable="false" primaryKey="true" />
</column>
<!-- 权限名称 -->
<column name="name" type="varchar(32)" />
<!-- 该权限需要访问的链接 -->
<column name="actionurl" type="varchar(256)" />
<!-- 该权限需要用到的js -->
<column name="jsurl" type="varchar(256)" />
<!-- 权限等级 1主菜单 2子菜单 3及以下按钮 -->
<column name="level" type="int" />
<!-- 菜单父级id -->
<column name="parentid" type="bigint"/>
<!-- 排序编号 -->
<column name="ordernum" type="int" defaultValue="0"/>
</createTable>
[size=large]这张是权限表,也是菜单(按钮)表,每个菜单都有他自己需要访问链接的权限,当菜单类型为子菜单或按钮时还需要配置该按钮对应的js文件,对没错,每个按钮都有自己独立的js文件,在用户没有按钮权限的时候,前端是js都不需要加载的
[b]添加菜单初始数据[/b]
[/size]
<insert tableName="permissions">
<column name="id" value="1"/>
<column name="name" value="用户管理" />
<column name="level" value="1" />
<column name="parentid" value="-1"/>
<column name="ordernum" value="1"/>
</insert>
<insert tableName="permissions">
<column name="id" value="101"/>
<column name="name" value="账号管理" />
<column name="actionurl" value="account!query.action" />
<column name="jsurl" value="user/account/searchGrid" />
<column name="level" value="2" />
<column name="parentid" value="1"/>
<column name="ordernum" value="1"/>
</insert>
<insert tableName="permissions">
<column name="id" value="102"/>
<column name="name" value="角色管理" />
<column name="actionurl" value="" />
<column name="jsurl" value="user/role/mainPanel" />
<column name="level" value="2" />
<column name="parentid" value="1"/>
<column name="ordernum" value="2"/>
</insert>
<insert tableName="permissions">
<column name="id" value="1001"/>
<column name="name" value="新增" />
<column name="actionurl" value="account!save.action" />
<column name="jsurl" value="user/account/searchGridAddBtn" />
<column name="level" value="3" />
<column name="parentid" value="101"/>
<column name="ordernum" value="1"/>
</insert>
<insert tableName="permissions">
<column name="id" value="1004"/>
<column name="name" value="修改" />
<column name="actionurl" value="account!save.action,account!find.action" />
<column name="jsurl" value="user/account/searchGridUpdBtn" />
<column name="level" value="3" />
<column name="parentid" value="101"/>
<column name="ordernum" value="1"/>
</insert>
[size=large]添加了一个父级菜单用户管理,两个子集菜单账号管理和角色管理,同时在账号管理下添加两个按钮新增和修改,需要注意的是新增的授权链接是account!save.action而修改的授权链接是account!save.action,account!find.action,因为修改需要先查再能改,当用户被授予新增和修改两个权限的时候,访问account!save.action,account!find.action也就不会受阻了
[b]创建角色表和角色权限关联表[/b]
[/size]
<createTable tableName="roles">
<column name="id" type="bigint">
<constraints nullable="false" primaryKey="true" />
</column>
<column name="rolename" type="varchar(32)" />
</createTable>
<insert tableName="roles">
<column name="id" value="1" />
<column name="rolename" value="超级管理员" />
</insert>
<createTable tableName="role_permission">
<column name="id" type="bigint">
<constraints nullable="false" primaryKey="true" />
</column>
<column name="perid" type="bigint" />
<column name="roleid" type="bigint" />
</createTable>
[size=large]角色和权限是多对多的关系,一个角色可以有多个权限,一个权限也可以属于多个角色,
配完初始数据以后前台展示大致是这样的(图片太大请在新窗口打开)[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3935/d7e898bc-1efd-34b3-ab94-8f6a3f974b0d.jpg[/img]
[size=large]
拥有角色管理的的用户可以添加删除角色,同时可以为没每个角色分配权限
[b]创建用户角色关联表[/b]
[/size]
<createTable tableName="account_role">
<column name="accountid" type="bigint" />
<column name="roleid" type="bigint" />
</createTable>
<insert tableName="account_role">
<column name="accountid" value="1" />
<column name="roleid" value="1" />
</insert>
[size=large]
这里给admin初始化了id为1的角色,也就是超级管理员,同样
拥有账号管理里的用户可以为账号添加角色
[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3944/6dfbee3e-fa7b-38f8-a9d9-4957e702118b.jpg[/img]
[size=large]到这里数据的配置基本讲完了,再展示下初始数据的liqiubase文件,大致就是那几张表,
最后我再给游客2分配了几个权限,新增一个角色为游客2的账号[/size]
[img]http://dl2.iteye.com/upload/attachment/0110/3948/d595180d-68b6-3a56-ab41-b109ec4ec99e.jpg[/img]
[img]http://dl2.iteye.com/upload/attachment/0110/4029/66a7093a-30df-3742-bd72-6053889ceabc.jpg[/img]
[img]http://dl2.iteye.com/upload/attachment/0110/3956/f9e4412e-70e6-386c-9b56-0adbf4eeedc5.jpg[/img]
[size=large]以上展示的只是数据配置的过程,通俗的讲就是想法,
现在想法已经出来了,权限也分配好了真正的权限控制核心代码就开始了,首先展示下我的hibernate entity
[/size]
package cn.sdh.entity;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.struts2.json.annotations.JSON;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import cn.sdh.common.base.AuditEntity;
@SuppressWarnings("serial")
@Entity
@Table(name="accounts")
public class Account extends AuditEntity implements UserDetails {
public Account(){
}
public Account(Long id, String username, String password, String email,Date lastLoginTime, int userType){
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.lastLoginTime = lastLoginTime;
this.userType = userType;
}
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACCOUNT")
@SequenceGenerator(name = "SEQ_ACCOUNT", sequenceName = "SEQ_ACCOUNT", initialValue = 2, allocationSize = 1)
@Column(name="id")
private Long id;
@Column(name="username")
private String username;
@Column(name="password")
private String password;
@Column(name="email")
private String email;
//@Type(type="cn.sdh.common.userType.dateToInt")
@Column(name="lastlogintime")
private Date lastLoginTime;
private int userType;//用户类型-1:超级管理员 0普通用户 1+级用户
//是否在线
@Transient
private int isOnLine;
//搜索时间 起止
@Transient
private Date lastLoginTime1;
@Transient
private Date lastLoginTime2;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name="account_role",joinColumns={@JoinColumn(name="accountid")},inverseJoinColumns={@JoinColumn(name="roleid")})
private List<Role> roles = new ArrayList<Role>();
@Transient
private File photo;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}
/**
* 告诉spring当前用户的角色
*/
@Override
@JSON(serialize=false)
public Collection<GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
for (Role role : this.getRoles()) {
authSet.add(new GrantedAuthorityImpl(role.getId().toString()));
}
return authSet;
}
@Override
@JSON(serialize=false)
public boolean isAccountNonExpired() {
return true;
}
@Override
@JSON(serialize=false)
public boolean isAccountNonLocked() {
return true;
}
@Override
@JSON(serialize=false)
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JSON(serialize=false)
public boolean isEnabled() {
return true;
}
public Date getLastLoginTime1() {
return lastLoginTime1;
}
public void setLastLoginTime1(Date lastLoginTime1) {
this.lastLoginTime1 = lastLoginTime1;
}
public Date getLastLoginTime2() {
return lastLoginTime2;
}
public void setLastLoginTime2(Date lastLoginTime2) {
this.lastLoginTime2 = lastLoginTime2;
}
public int getUserType() {
return userType;
}
public void setUserType(int userType) {
this.userType = userType;
}
public int getIsOnLine() {
return isOnLine;
}
public void setIsOnLine(int isOnLine) {
this.isOnLine = isOnLine;
}
@JSON(serialize=false)
public List<Permission> getSignPer(){
List<Permission> perList = new ArrayList<Permission>();
for(Role role : this.getRoles()){
List<Permission> sublist = role.getPermissionList();
for(int h = 0; h<sublist.size(); h++){
boolean continueFlag = false;
for(int j = 0;j<perList.size();j++){
if(role.getPermissionList().get(h).getId()==perList.get(j).getId()){
continueFlag = true;
break;
}
}
if(continueFlag){
continue;
}
if(role.getPermissionList().get(h).getLevel()==2){
role.getPermissionList().get(h).setLeaf(1);
}
role.getPermissionList().get(h).setChildren(null);
//role.getPermissionList().get(h).setControlType(role.getPermissionList().get(h).getControlType());
perList.add(role.getPermissionList().get(h));
}
}
return perList;
}
public File getPhoto() {
return photo;
}
public void setPhoto(File photo) {
this.photo = photo;
}
}
package cn.sdh.entity;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.struts2.json.annotations.JSON;
import cn.sdh.common.base.AuditEntity;
/**
* 权限类(菜单,按钮,功能)
* @author 孙东辉
*/
@Entity
@Table(name="permissions")
public class Permission extends AuditEntity{
/**
*
*/
private static final long serialVersionUID = 7930766554705439940L;
@Id
@Column(name="id")
private Long id;
@Column(name="name")
private String name;
/**
* 访问地址
*/
@Column(name="actionurl")
private String actionUrl;
/**
* js文件地址
*/
@Column(name="jsurl")
private String jsUrl;
/**
* 等级 1-N
*/
@Column(name="level")
private int level;
/**
* 父级编号
*/
@Column(name="parentid")
private Long parentid;
/**
* 弹出的控件类
*/
@Transient
private String controlType;
@Transient
private int leaf;
@Transient
private boolean checked;
@Transient
private boolean expanded = true;
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name = "parentid", referencedColumnName="id",insertable=false,updatable=false)
private List<Permission> children;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getActionUrl() {
return actionUrl;
}
public void setActionUrl(String actionUrl) {
this.actionUrl = actionUrl;
}
public String getJsUrl() {
return jsUrl;
}
public void setJsUrl(String jsUrl) {
this.jsUrl = jsUrl;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public Long getParentid() {
return parentid;
}
public void setParentid(Long parentid) {
this.parentid = parentid;
}
@JSON(name="text")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getControlType() {
if(this.jsUrl!=null)
return getControlTypeByDg(this.jsUrl);
else
return this.controlType;
}
public void setControlType(String controlType) {
this.controlType = controlType;
}
public int getLeaf() {
if(this.level==3){
return 1;
}
return this.leaf;
}
public void setLeaf(int leaf) {
this.leaf = leaf;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
public List<Permission> getChildren() {
return children;
}
public void setChildren(List<Permission> children) {
this.children = children;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
/**
* 通过路径获得组件名称
* @param path
* @return
*/
public String getControlTypeByDg(String path){
if(path.indexOf("/")>=0){
path = path.replaceFirst(path.substring(path.indexOf("/"),path.indexOf("/")+2), path.substring(path.indexOf("/")+1,path.indexOf("/")+2).toUpperCase());
return getControlTypeByDg(path);
}else{
return path;
}
}
}
package cn.sdh.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OrderBy;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import cn.sdh.common.base.AuditEntity;
/**
* 角色类
* @author 孙东辉
*/
@Entity
@Table(name="roles")
public class Role extends AuditEntity{
/**
*
*/
private static final long serialVersionUID = 5459879676154477558L;
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ROLE")
@SequenceGenerator(name = "SEQ_ROLE", sequenceName = "SEQ_ROLE", initialValue = 100, allocationSize = 1)
private Long id;
/**
* 角色名称
*/
@Column(name="rolename")
private String roleName;
/**
* 拥有的权限集合
*/
@ManyToMany(fetch = FetchType.EAGER)
@Fetch(FetchMode.SUBSELECT)
@OrderBy("level")
@JoinTable(name="role_permission",joinColumns={@JoinColumn(name="roleid")},inverseJoinColumns={@JoinColumn(name="perid")})
private List<Permission> permissionList = new ArrayList<Permission>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public List<Permission> getPermissionList() {
return permissionList;
}
public void setPermissionList(List<Permission> permissionList) {
this.permissionList = permissionList;
}
}
[size=large]Account作为springsecrity的账户类必须实现UserDetails接口,实现的getAuthorities方法是告诉spring当前用户的角色,还有个自定义方法getSignPer用于获取当前用户的权限列表,Permission类有个方法getControlTypeByDg通过配置的jsUrl链接获取Extjs的组件类名,他们的用处后面就能看到,这里补充下我用的是Hibernate的注解方式,严格来讲项目中除了公共配置部分,其他包括Struts Spring等需要xml配置的我全用了注解,如果你对注解不是很了解用xml配置的形式也是可以的
用户通过springsecrity登录成功的时候,会把Account对象保存它的上下文,然后访问这个跳转到主页的action方法,action方法中从springsecrity上下文中获取到Account后通过getSignPer方法获取当前用户的权限(也就是菜单),下面就是用户cccq3y的菜单了
[/size]
/**
* 跳转到主页
*
* @throws JSONException
*/
public String index() throws JSONException {
entity = SpringSecurityUtils.getCurrentUser();
List<Permission> perList = entity.getSignPer();
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("perList", perList);
request.setAttribute("perListJson", JSONUtil.serialize(perList));
return "index";
}
[img]http://dl2.iteye.com/upload/attachment/0110/4020/9f8e88f9-fd69-3a63-a9ec-34ac79a172d8.jpg[/img]
[size=large]当用户点击新增确认按钮时,浏览器会向后台提交一个表单,url为account!save.action,首先会被springsecrity的MyFilterInvocationSecurityMetadataSource拦截到[/size]
package cn.sdh.audit;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import cn.sdh.common.exception.ServiceException;
import cn.sdh.entity.Permission;
import cn.sdh.entity.Role;
import cn.sdh.service.RoleService;
import cn.sdh.utils.MyCacheUtil;
/**
* 告诉spring当前访问链接 需要什么权限 在这边定义
* @author 孙东辉
*
*/
@Service
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
private RoleService roleService;
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
//这个是当前访问的链接
String requestUrl = ((FilterInvocation) object).getRequestUrl();
//设置哪些页面不需要权限就可访问
if(requestUrl.indexOf("account!index.action") >= 0
|| requestUrl.indexOf("index.jsp") >= 0
|| requestUrl.indexOf("permission!queryBtnListByControlType.action") >= 0
|| requestUrl.indexOf("account!searchOnlineList.action")>=0
|| requestUrl.indexOf("queryDicDataByKey.action")>=0){
return null;
}
// 对于带参数的请求,截取?前面的uri
int urlIndex = requestUrl.indexOf("?");
if (urlIndex != -1) {
requestUrl = requestUrl.substring(0, urlIndex);
}
//如果第一位是/ 去掉
if(requestUrl.startsWith("/")){
requestUrl = requestUrl.substring(1);
}
//从缓存中取当前系统的所有角色
List<Role> roles = (List<Role>) MyCacheUtil.getRolesList();
if(roles == null || roles.size()==0){//如果缓存中没有
try {
roles = roleService.query();
//把角色放入缓存
MyCacheUtil.setRolesList(roles);
} catch (ServiceException e) {
throw new IllegalArgumentException(e.getMsg());
}
}
Collection<ConfigAttribute> c = new HashSet<ConfigAttribute>();
//遍历所有角色
List<Permission> permissionList = null;
if(roles.size()>0){
for(int i = 0;i<roles.size();i++){
permissionList = roles.get(i).getPermissionList();
for(int j = 0;j<permissionList.size();j++){
//如果权限中的actionUrl包含当前访问的链接,当说明现在这个角色是可以访问该链接的
if(permissionList.get(j)!=null&&permissionList.get(j).getActionUrl()!=null&&permissionList.get(j).getActionUrl().indexOf(requestUrl)>=0){
ConfigAttribute configAttribute = new SecurityConfig(roles.get(i).getId()+"");
c.add(configAttribute);
break;
}
}
}
}else{
return null;
}
//最后返回的是一个角色的集合,并且这些角色都是可以访问该链接的
return c;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
}
[size=large]返回的角色中应该有一个是游客2的id,然后再被MyAccessDecisionManager拦截到[/size]
package cn.sdh.audit;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
/**
* 自定义访问决定器 控制用户能否访问该链接
* @author sun
*
*/
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
//如果访问的页面没有权限限制 那么不做拦截继续访问 如果未空说明该页面不需要任何角色
// 注意这里的configAttributes 其实就是MyFilterInvocationSecurityMetadataSource返回的
if (configAttributes == null) {
return;
}
//如果有权限限制 那么需要迭代权限 验证用户所拥有的权限是否包含 当前页面的访问权限
Iterator<ConfigAttribute> ite = configAttributes.iterator();
while (ite.hasNext()) {
//获取访问页面需要的权限
ConfigAttribute ca = ite.next();
//获取访问页面需要的权限的权限名称
String needRole = ((SecurityConfig) ca).getAttribute();
//迭代用户所拥有的权限
for (GrantedAuthority ga : authentication.getAuthorities()) {
//如果用户的其中一个权限满足 访问链接需要的其中一个权限,那么访问将继续
if (needRole.equals(ga.getAuthority())) {
return;
}
}
}
//否则 访问将被终止
throw new AccessDeniedException("没有权限访问");
}
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> clazz) {
// TODO Auto-generated method stub
return true;
}
}
[size=large]注释写个很清楚了,把当前用户所拥有的角色和MyFilterInvocationSecurityMetadataSource返回的访问当前链接需要哪些角色,如果包含说明当前用户能访问该链接了,再没抛AccessDeniedException错误的情况下验证都通过
最后再说下前端代码,首先主页的jsp要根据权限加载js文件
[/size]
<!-- 根据权限加载js文件 -->
<s:iterator value="#request.perList" var="bean">
<s:if test="#bean.jsUrl!=null">
<script type="text/javascript" src="common/jscode/<s:property value="#bean.jsUrl"/>.js"></script>
</s:if>
</s:iterator>
[size=large]然后来感受下extjs4的强大之处了,首先我封装一个公共的权限工具条[/size]
/**
* 权限按钮工具条
*/
Ext.define("app.base.auditbar", {
extend : "Ext.toolbar.Toolbar",
alias : 'widget.auditbar',
initComponent : function() {
var me = this;
var call = function(){
var sme = me;
// 根据组件类型 获取它所有的按钮
Ext.Ajax.request({
url : 'permission!queryBtnListByControlType.action',
params : {
'entity.controlType' : me.compont
},
success : new app.ajaxHand({
success : function(result,scope) {
var btns = result.searchList;
for ( var i = 0; i < btns.length; i++) {
if(scope){
sme = scope;
}
sme.insert(i, [ {
xtype : btns[i].controlType,
text : btns[i].text
} ]);
}
}
},sme)
});
};
if(me.remote){
call();
}
if(me.searchItems){
if(!me.items){
me.items = [];
}
me.items =me.items.concat(['->', {
xtype : "searchItems",
items : me.searchItems
} ]);
}
me.callParent(me);
}
});
[size=large]然后是grid基类[/size]
Ext.define("app.base.grid", {
extend : "Ext.grid.Panel",
//日期
dateRender : function(){return Ext.util.Format.dateRenderer(app.constant.date_render);},
//日期加时间
datetimeRender : function(){return Ext.util.Format.dateRenderer(app.constant.datetime_render);},
//单元格编辑插件
editingPlugin : function(){ return Ext.create('Ext.grid.plugin.CellEditing',{});},
//行编辑插件
rowEditingPlugin : function(){return Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
errorSummary : false,
autoCancel: true
});},
//行复选框
selModelPlugin : function(){return Ext.create("Ext.selection.CheckboxModel");},
initComponent : function(){
var me = this;
if(me.paging){
me.bbar = Ext.create('app.base.paging',{
store:me.store
});
}
me.tbar = Ext.create('app.base.auditbar',{
compont : me.xtype,
searchItems : me.searchItems,
remote:me.auditbarEnable,
items : me.btns
});
this.callParent();
},
//添加行
addRow : function(){
this.store.insert(0,new (Ext.ModelManager.getModel(this.model)));
this.editing.cancelEdit();
if(this.editing){
this.editing.startEdit(0,0);
}
},
//删除行
deleteRow : function(){
var recode = this.getSelectionModel().getSelection();
this.store.remove(recode);
},
//验证选中行
checkSelectRow : function(count,tip){
if(!count){
count = 1;
}
tip = tip || "请"+(count!=1?"至少":"")+"选择一行要删除的记录";
var recode = this.getSelectionModel().getSelection();
if(recode.length==0||(count==1&&recode.length>1)){
app.alert(tip);
return false;
}
var usedata = [];
var ids = "";
for(var i = 0;i<recode.length;i++){
usedata.push(recode[i].data);
ids += recode[i].data.id;
if(i!=recode.length-1){
ids += ",";
}
}
usedata.unshift(ids);
return usedata;
}
});
[size=large]把当前组件的xtype作为参数,到后台获取对应的父级菜单,再根据用户拥有的按钮权限获取这个用户在当前组件的按钮权限,那么问题来了,我是如果通过xtype定位到那条权限数据的呢,真相是我在配置账号管理这个权限的时候配置的jsUrl为user/account/searchGrid然后通过上面Permission类自定义的getControlTypeByDg方法转成xtype,再来看看我的grid子类就清楚了[/size]
Ext.define("app.user.account.searchGrid", {
extend : "app.user.account.grid",
alias: 'widget.userAccountSearchGrid',
initComponent : function(){
var me = this;
me.editing = me.rowEditingPlugin();
Ext.apply(this,{
plugins : [me.editing],
paging : true,
selModel : me.selModelPlugin(),
searchItems :[{
fieldLabel : '用户名',
xtype : 'textfield',
labelAlign : 'right',
name : 'entity.username',
labelWidth : 45
},{
fieldLabel : '邮箱',
xtype : 'textfield',
labelAlign : 'right',
name : 'entity.email',
labelWidth : 45
},{
fieldLabel : '上次登录起',
xtype : 'datefield',
labelAlign : 'right',
name : 'entity.lastLoginTime1',
labelWidth : 75
},{
fieldLabel : '上次登录止',
xtype : 'datefield',
labelAlign : 'right',
name : 'entity.lastLoginTime2',
labelWidth : 75
}]
});
me.callParent();
}
});
[size=large]alias:widget.userAccountSearchGrid,这样就能和后台的权限做对应了,而且在点击菜单树的时候同样用到这个功能,我会根据当前被点击菜单的jsUrl属性转义成xtype,然后很方便的加载对应的组件,就这么简单.最后展示下我的js文件目录
[img]http://dl2.iteye.com/upload/attachment/0110/4032/fa508ba8-ae76-36ea-bbce-faa413ed1be7.jpg[/img]
写到这里差不多就结束了,写的有点乱,看不懂的可以加我qq探讨一下;
[/size]