自定义标签 + shiro 实现权限细粒度控制
这里我们是使用shiro来实现登录验证,授权等操作,然后利用自定义jsp标签来实现权限菜单的细力度控制。所谓的细粒度控制,就是根据用户登录权限的不同,显示不同的菜单,例如,用户如果有添加用户,修改用户的权限,我们就显示这个俩个菜单,然后我们并不显示删除用户的菜单。
如何自定义jsp标签
1.定义一个权限标签,命名为mytag.tld
/ Permission Tag Library 1.0 m /my-tags 权限控制标签 auth com.xxx.core.tag.PrivilegeTag JSP privilege true true String
2.将该xml文件放于/WEB-INF/tlds下,并实现标签控制类
/** * 重写权限标签控制类 * @author liuxg * @date 2015年8月24日 上午9:13:15 */ public class PrivilegeTag extends TagSupport { private static final long serialVersionUID = 1L; private String privilege; //标签属性 @Override public int doStartTag() { User user = AuthUtil.getCurrentUser();//获取登录用户信息 if(user == null) return SKIP_BODY; if (isManager(user)) return EVAL_BODY_INCLUDE; //超级管理员获取所有权限 boolean bResult = SecurityUtils.getSubject().isPermitted(privilege);//根据标签属性判断用户是否有此菜单功能权限,isPermitted的调用会触发doGetAuthorizationInfo方法 if(bResult){ return EVAL_BODY_INCLUDE; } return SKIP_BODY; } /** * 判断用户是否超级管理员 * @return */ private boolean isManager(User user){ List roles = user.getRoles(); boolean b = false ; for (Role role : roles) { //遍历是否有超级管理员角色 if (role.getIsManager() == Constants.MANAGER_CODE) { b = true ; break ; } } String accountName = user.getAccountName(); if (accountName.equals(Constants.ADMIN_ACCOUNT) || accountName.equals(Constants.SYSADMIN_ACCOUNT) || b) { return true; } return false; } public String getPrivilege() { return privilege; } public void setPrivilege(String privilege) { this.privilege = privilege; } }
3.接下来还需要在web.xml设置我们自定义的标签路径
/my-tags /WEB-INF/tlds/my-tag.tld
到此我们就完成了标签的自定义,那在jsp页面上,我们只需要引入我们自定义的标签库,并在需要控制的html标签上使用我们的权限标签即可
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="m" uri="/gosun-tags" %>
//... //...
//...
//...
//...
通过
包裹的html标签,我们可以在实现类中,通过代码控制显示与否。
使用shiro来进行用户的权限登录和授权
1.因为web项目而且使用spring,我们就先把shiro和spring整合起来吧。
/front/** = anon /loginsubmit.do = authc /logout.do = logout /backer/** = user
这是spring-shiro的配置文件,需要加入到web.xml的contextConfigLocation里面,接下来看看怎么写登录的jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2.定义Realm,进行权限验证和登录授权
import java.util.ArrayList; import java.util.List; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import com.gosun.backer.permission.entity.Permission; import com.gosun.backer.role.entity.Role; import com.gosun.backer.user.entity.User; import com.gosun.backer.user.service.UserMgrService; import com.gosun.core.utils.Constants; import com.gosun.util.auth.AuthUtil; /** * 用户登录进去的域 * @author liuxg * @date 2016年5月30日 下午4:02:06 */ public class GIPSRealm extends AuthorizingRealm { @Autowired UserMgrService userService; /** * 对当前路径予权限和角色,因为配置ehcache,所以可以把用户权限角色信息缓存起来 * 当用户调用Security.getSubject().isPermitted(permissions),ecurity.getSubject().hasRole(roleIdentifier) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { User user = AuthUtil.getCurrentUser(); SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo(); simpleAuthorInfo.addRoles(getRoles(user)); simpleAuthorInfo.addStringPermissions(getPermCodes(user)); return simpleAuthorInfo; } /** * 获取权限,string存放的是权限编码 * @param user * @return */ private List getPermCodes(User user) { List perms = new ArrayList(); List roles = user.getRoles(); for (Role role : roles) { List_perms = role.getPermissions(); for (Permission _perm : _perms) { perms.add(_perm.getPermCode()); } } return perms; } /** * 获取角色集合,string存放的角色名称 * @param user * @return */ private List getRoles(User user) { List roles = new ArrayList(); for (Role role : user.getRoles()) { roles.add(role.getRoleName()); } return roles; } /** * 登录认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken)authcToken; User user = userService.findByAccountName(token.getUsername()) ;//通过帐号获取用户实例 if (user != null && ByteSource.Util.bytes(token.getPassword()) .equals(ByteSource.Util.bytes(user.getPassword()))) {//用户校验 setSessionInfo(user); return new SimpleAuthenticationInfo(user.getAccountName(), user.getPassword(), user.getNickName()); //验证成功之后进行授权 } return null ; } /** * 存放一些信息到session中,便于获取,可以通过httpsession获取相应的信息 * @param user */ @SuppressWarnings("unused") private void setSessionInfo(User user){ Subject sub = SecurityUtils.getSubject(); Session session = sub.getSession(); //显示的设置权限和角色,避免下次再去数据库获取,提高效率 List roles = user.getRoles(); for (int i = 0; i < roles.size(); i++) { Role role = roles.get(i); List perms = role.getPermissions(); for (Permission permission : perms) {} role.setPermissions(perms); roles.set(i,role); } user.setRoles(roles); session.setAttribute(Constants.CURRENT_USER, user); } }
这里我们还可以自定义一个form拦截器,可以在验证之前做一些东西,例如验证码验证等等
import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; /** * 自定义登录拦截器,可以在shiro调用自身登录之前做一些操作 * @author liuxg * @date 2016年5月30日 下午8:10:56 */ public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { return super.onAccessDenied(request, response); } }
接下来,我们来看看,定义的几个实体类,权限,角色和用户实体类
首先是我们的用户实体类
/** * 用户实体类 * @author liuxg * @date 2016年6月1日 下午2:36:13 */ @SuppressWarnings("serial") @Entity @Where(clause = "is_deleted = 0") @Table(name = "tb_user") public class User extends IdEntity{ private String accountName ; private String password ; private String nickName ; private String mobilePhone ; private String email ; private Byte isDeleted ; private Date createTime ; private List roles ; public User(String accountName, String password, String nickName, String mobilePhone, String email, Byte isDeleted, Date createTime) { super(); this.accountName = accountName; this.password = password; this.nickName = nickName; this.mobilePhone = mobilePhone; this.email = email; this.isDeleted = isDeleted; this.createTime = createTime; } public User(){} ; @Column(name = "account_name") public String getAccountName() { return accountName; } public void setAccountName(String accountName) { this.accountName = accountName; } @Column(name = "password") public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Column(name = "mobile_phone") public String getMobilePhone() { return mobilePhone; } public void setMobilePhone(String mobilePhone) { this.mobilePhone = mobilePhone; } @Column(name = "email") public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Column(name = "is_deleted") public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } @Column(name = "create_time") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @ManyToMany(mappedBy = "users") public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; } @Column(name = "nick_name") public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } }
再然后是角色实体类
/** * 角色实体类 * @author liuxg * @date 2016年6月1日 下午2:35:37 */ @SuppressWarnings("serial") @Entity @Where(clause = "is_deleted = 0") @Table(name = "tb_role") public class Role extends IdEntity { private String roleName ; private String roleDesc ; private Byte isDeleted ; private Byte isManager ; private Date createTime ; private Listpermissions ; private List users ; public Role(String roleName, String roleDesc, Byte isDeleted, Byte isManager, Date createTime) { super(); this.roleName = roleName; this.roleDesc = roleDesc; this.isDeleted = isDeleted; this.isManager = isManager; this.createTime = createTime; } public Role(){} ; @Column(name = "role_name") public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } @Column(name = "role_desc") public String getRoleDesc() { return roleDesc; } public void setRoleDesc(String roleDesc) { this.roleDesc = roleDesc; } @Column(name = "is_deleted") public Byte getIsDeleted() { return isDeleted; } public void setIsDeleted(Byte isDeleted) { this.isDeleted = isDeleted; } @Column(name = "is_manager") public Byte getIsManager() { return isManager; } public void setIsManager(Byte isManager) { this.isManager = isManager; } @Column(name = "create_time") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name="tb_user_role_conf", joinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="id") }) public List getUsers() { return users; } public void setUsers(List users) { this.users = users; } @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name="tb_role_permission_conf", joinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="permisson_id",referencedColumnName="id") }) public List getPermissions() { return permissions; } public void setPermissions(List permissions) { this.permissions = permissions; } }
最后是权限实体类
/** * 权限菜单实体类 * @author liuxg * @date 2016年6月1日 下午2:35:37 */ @SuppressWarnings("serial") @Entity @Table(name = "tb_permission") public class Permission extends IdEntity{ private String permName ; private String permCode ; private String permPath ; private Byte permType ; //权限类型 0一级菜单 1二级菜单 2功能菜单(功能菜单可以没有路径) private Integer orderNo ; //菜单排序号,关系用户登录时候,关系到首页显示具体哪个菜单 private Permission parent ; private List roles ; public Permission(String permName, String permPath, Byte permType ,Permission parent) { super(); this.permType = permType ; this.permName = permName; this.permPath = permPath; this.parent = parent; } public Permission(){}; @Column(name= "perm_name") public String getPermName() { return permName; } public void setPermName(String permName) { this.permName = permName; } @Column(name= "perm_path") public String getPermPath() { return permPath; } public void setPermPath(String permPath) { this.permPath = permPath; } @OneToOne(fetch=FetchType.LAZY) @JoinColumn(name="parent_id") public Permission getParent() { return parent; } public void setParent(Permission parent) { this.parent = parent; } @ManyToMany(mappedBy = "permissions") public List getRoles() { return roles; } public void setRoles(List roles) { this.roles = roles; } @Column(name= "perm_code") public String getPermCode() { return permCode; } public void setPermCode(String permCode) { this.permCode = permCode; } @Column(name= "perm_type") public Byte getPermType() { return permType; } public void setPermType(Byte permType) { this.permType = permType; } @Column(name= "order_no") public Integer getOrderNo() { return orderNo; } public void setOrderNo(Integer orderNo) { this.orderNo = orderNo; } }
这里还有一个问题,就是当当用户登录,有某些标签权限,那用户登录之后,就必须根据这些权限,路由到某个路径下,这里就需要做一个权限路径的路由方法。
/** * 后台主页路径导航controller * @author liuxg * @date 2016年5月30日 下午5:56:58 */ @RestController @RequestMapping("/backer/auth") public class AuthController { @Autowired AuthService loginService; /** * 登录成功之后进来这里,根据用户的角色和权限进行菜单路径路由 * @return */ @RequestMapping("/homePage") public ModelAndView homePage() { Listperms = AuthUtil.getCurrentUserPermissions(); String path = perms.get(0).getPermPath(); return new ModelAndView(path); //这里假设路由到user } }
这里我们的AuthUtil这么定义的
/** * httpClient工具类 * @author liuxg * @date 2016年6月1日 下午6:29:07 */ public class AuthUtil { /** * 获取当前用户 * @return */ public static User getCurrentUser(){ Subject sub = SecurityUtils.getSubject(); Session session = sub.getSession(); User user = (User) session.getAttribute(Constants.CURRENT_USER); return user ; } /** * 在用户中获取权限信息 * @return */ public static ListgetCurrentUserPermissions() { User user = AuthUtil.getCurrentUser(); List perms = new ArrayList (); for (Role role : user.getRoles()) { for (Permission permission : role.getPermissions()) { perms.add(permission); } } perms = filterRepAndSort(perms); return perms; } /** * 去重,排序 * @param perms * @return */ private static List filterRepAndSort(List perms) { for ( int i = 0 ; i < perms.size() - 1 ; i++ ) { for ( int j = perms.size() - 1 ; j > i; j-- ) { if (perms.get(j).getOrderNo() == perms.get(i).getOrderNo()) { perms.remove(j); } } } Collections.sort(perms, new Comparator () { @Override public int compare(Permission p1, Permission p2) { return p1.getOrderNo() - p2.getOrderNo(); } }); return perms; } }
功能就暂时实现到这里,这里细粒度的作用,可以让我们把权限限制到html的任何一个标签上,用户没有权限的标签,我们可以直接隐藏,不给用户操作,用户也不可见。请多指教
上一篇: vue内置指令详解
推荐阅读
-
自定义标签 + shiro 实现权限细粒度控制
-
JAVAEE——BOS物流项目11:在realm中授权、shiro的方法注解权限控制、shiro的标签权限控制、总结shiro的权限控制方式、权限管理
-
基于Vue自定义指令实现按钮级权限控制思路详解
-
Springboot自定义注解实现简单的接口权限控制,替代Shiro/SpringSecurity
-
Springboot + Vue + shiro 实现前后端分离、权限控制
-
自定义jsp标签实现按钮权限控制
-
自定义标签 + shiro 实现权限细粒度控制
-
自定义Hive权限控制(4) 扩展Hive以实现自定义权限控制
-
自定义Hive权限控制(3) 扩展Hive以实现自定义权限控制
-
Springboot + Vue + shiro 实现前后端分离、权限控制