欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

自定义标签 + shiro 实现权限细粒度控制

程序员文章站 2022-05-21 14:49:41
这里我们是使用shiro来实现登录验证,授权等操作,然后利用自定义jsp标签来实现权限菜单的细力度控制。所谓的细粒度控制,就是根据用户登录权限的不同,显示不同的菜单,例如,用户如果...

这里我们是使用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 List permissions ;
        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() {
            List perms = 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 List  getCurrentUserPermissions() {
    
            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的任何一个标签上,用户没有权限的标签,我们可以直接隐藏,不给用户操作,用户也不可见。请多指教