云悦智销项目07_权限与菜单树展示权限

一.权限管理

1.1 真实权限配置
1.1.1 拦截真实权限

public class ShiroFilterMapFactory {

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createMap(){
        ...
        //从数据库中拿到所有权限
        List<Permission> perms = permissionService.findAll();
        //遍历所有权限,设置权限拦截
        perms.forEach(p->{
            //perms:权限拦截
            map.put(p.getUrl(),"perms["+p.getSn()+"]");
        });
        ...
    }
}

1.1.2 当前用户赋权

PermissionRepository:根据用户id获取权限
    /**
     * 根据当前用户获取相应的权限(只拿权限中的sn)
     * JPQL关连法则(2): 1.关连的是前面的对象别名.属性 2.自动消除迪卡尔积
     *  select o from Employee o join o.department
     */
    @Query("select distinct p.sn from Employee e join e.roles r join r.permissions p where e.id =?1")
    Set<String> findSnByUser(Long userid);

PermissionService:根据用户id获取权限
//根据当前登录用户拿到对应的权限
@Override
public Set<String> findSnByLoginUser() {
    //从shiro中拿到主体(当前登录用户)
    Subject subject = SecurityUtils.getSubject();
    Employee loginUser = (Employee)subject.getPrincipal();
    return permissionRepository.findSnByUser(loginUser.getId());
}

JpaRealm:获取权限
@Autowired
private IEmployeeService employeeService;
@Autowired
private IPermissionService permissionService;

//授权认证功能就写在这里面
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    //从数据库中获取权限并放且放到授权对象中
    Set<String> perms = permissionService.findSnByLoginUser();
    authorizationInfo.setStringPermissions(perms);
    return authorizationInfo;
}

二.ajax请求权限拦截

解决Ajax的跳转问题
2.1 自定义权限

写一个类,继承 PermissionsAuthorizationFilter
重写 onAccessDenied
/**
 * 重写Shiro的权限过滤器(我们要让他支持Ajax操作)
 */
public class AiSellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    //请求头的所有数据都封装到请求对象中
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        System.out.println("我是一个中国人!!!!!");
        Subject subject = this.getSubject(request, response);
        if (subject.getPrincipal() == null) {
            this.saveRequestAndRedirectToLogin(request, response);
        } else {
            //确定是否是Ajax请求,如果是, 就返回json数据,如果不是,执行原来的代码
            //1.把请求和响应对象转到Http的方式(功能更强大一些)
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            //2.根据请求对象拿到请求头中 X-Requested-With:XMLHttpRequest
            String xRequestedWith = req.getHeader("X-Requested-With");
            if("XMLHttpRequest".equals(xRequestedWith)){
                //3.Ajax请求的处理,返回一个json数据 {success:false,msg:"没有权限"}
                //设置响应头为json
                resp.setContentType("application/json;charset=UTF-8");
                //通过一个响应流把json数据返回回去
                resp.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
            }else {
                //获取到没有权限的地址
                String unauthorizedUrl = this.getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    //如果有地址就跳转
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    //如果没有地址就报401错误
                    WebUtils.toHttp(response).sendError(401);
                }
            }
        }

        return false;
    }
}

2.2 权限配置

   <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        ...
        <!--告诉Shiro我这里有一个自定义过滤器-->
        <property name="filters">
            <map>
                <entry key="aisellPerms" value-ref="aiSellPermissionsAuthorizationFilter" />
            </map>
        </property>
    </bean>

    <!--配置我们的自定义权限过滤器-->
    <bean id="aiSellPermissionsAuthorizationFilter"
          class="cn.itsource.aisell.web.shiro.AiSellPermissionsAuthorizationFilter">
    </bean>

2.3 权限使用

权限拦截的地方写上 aisellPerms 对应上一步的配置名称
public class ShiroFilterMapFactory {

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createMap(){
       ...
        //遍历所有权限,设置权限拦截
        perms.forEach(p->{
            //perms:权限拦截
            map.put(p.getUrl(),"aisellPerms["+p.getSn()+"]");
        });
        ...
    }
}

三.shiro标签认识

3.1 引入标签

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

3.2 拿到主体的标签
<shiro:principal property="username" />  

3.3 权限判断的标签
<shiro:hasPermission name="employee:delete">
        <a href="javascript:;" data-method="delete" class="easyui-linkbutton" data-options="iconCls:'icon-remove',plain:true">删除</a>
</shiro:hasPermission>

四.菜单管理

3.1 菜单对象

自己完成基本CRUD
/**
 * 菜单
 */
@Entity
@Table(name = "menu")
public class Menu extends BaseDomain {

    //菜单名称
    private String name;
    //菜单路径
    private String url;
    //图标
    private String icon;

    //parent_id
    //配置一对多
    @OneToMany
    @JoinColumn(name = "parent_id")
    private List<Menu> children = new ArrayList<>();
    ...
}

3.2 获取父菜单
3.2.1

MenuRepository拿到所有父菜单
/**
 * 获取到所有父菜单
 * @return
 */
@Query("select o from Menu o where o.url is null ")
List<Menu> findParentMenus();

3.2.2 service与controller的支持

service
@Override
public List<Menu> findParentMenus() {
    return menuRepository.findParentMenus();
}
controller
/**
 * 返回所有的父菜单
 * @return
 */
@RequestMapping("/findParentMenus")
@ResponseBody
public List<Menu> findParentMenus() {
    return menuService.findParentMenus();
}

3.2.3 主页面进行访问
/

/创建咱们的菜单树
$('#menuTree').tree({
    url:'/menu/findParentMenus',
    ...
    })
  权限菜单
每个用户登录后是根据自己的权限查找到相应的菜单
1.1 Role的修改
@Transient:一对多不需要JPA管理,由我们自己添加数据
@JsonIgnore:双向关连不要生成JSON,会搞成死循环,根据业务需求放某一方放弃生成
@Entity
@Table(name = "menu")
public class Menu extends BaseDomain {

    //菜单名称
    private String name;
    //菜单路径
    private String url;
    //图标
    private String icon;

    /**
     * 配置多对一(性能,业务需求【需要通过子菜单拿到父菜单】)
     *  让生成JSON的时候,不要去找父亲
     */
    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonIgnore
    private Menu parent;

    /**
     * 不要配置单向一对多(性能不好)
     * 你现在敢配置一对多嘛?配置了一对多,关系则由JPA来管理!
     * 它会自动从1方去拿到所有多方!!!
     *   1.这里是否需要儿子的List字段 -》 一定要的
     *   2.这个儿子的List是否要JPA管理 -》 不可以
     *   加上这个字段(属性),但是不交给JPA管理【咱们手动管理这个字段】
     */
    //parent_id
    //配置一对多 Transient:临时属性(JPA不会管它)
    @Transient
    private List<Menu> children = new ArrayList<>();

    ...
}

1.2 MenuRepository
根据员工获取到他对应的所有权限菜单(这里只有子菜单)
这条JPQL要求你的关系都是配置好的(与实体类匹配上)

 /**
     * 根据用户拿到对应的权限菜单
     */
    @Query("select distinct m from Employee e join e.roles r join r.permissions p join p.menu m where e.id=?1")
    List<Menu> findMenusByUser(Long userId);

1.3 MenuService
拿到当前登录用户然后进行查询
拼接成前台需要的效果(一定要记住你最后想要的是什么效果)

    @Override
    public List<Menu> findLoginUserMenus() {
        //一.准备一个装父菜单的容器(List)
        List<Menu> parentMenuList = new ArrayList<>();
        //二.拿到并循环子菜单
        //2.1 拿到当前登录用户
        Subject subject = SecurityUtils.getSubject();
        Employee loginUser = (Employee) subject.getPrincipal();
        //2.2 根据用户拿到他的菜单 iter
        List<Menu> menus = menuRepository.findMenusByUser(loginUser.getId());
        for (Menu menu : menus) {
            //三.开始设置数据结构
            //3.1 根据子菜单拿父菜单
            Menu parentMenu = menu.getParent();
            //3.2 判断父菜单容器中是否有这个父菜单
            if(!parentMenuList.contains(parentMenu)){
                //如果没有,我们要把当前这个父菜单放到容器中
                parentMenuList.add(parentMenu);
            }
            //3.3 把子菜单放到父菜单中
            parentMenu.getChildren().add(menu);
        }
        return parentMenuList;
    }

1.4 前台正常展示
@RequestMapping("/findParentMenus")
@ResponseBody
public List<Menu> findParentMenus() {
    return menuService.findLoginUserMenus();
}              

你可能感兴趣的:(项目搭建)