智销系统day07-权限&菜单

一.权限管理

1.1 真实权限配置

1.1.1 拦截真实权限

public class FilterChainDefinitionMapFactory {
    @Autowired
    private IPermissionService permissionService;
    /**
     * 这个方法就会返回咱们的权限数据(它是有顺序)
     */
    public Map<String,String> creatMap(){
        //注意:这里的map是有顺序的
        Map<String,String> map = new LinkedHashMap<>();
        map.put("/login","anon");
        //静态资源放行
        map.put("/*.css","anon");
        map.put("/*.js","anon");
        map.put("/easyui/**","anon");
        map.put("/css/**","anon");
        map.put("/images/**","anon");
        map.put("/js/**","anon");
        //map.put("/json/**","anon");
        //获取所有权限,拦截真实权限
        List<Permission> perms = permissionService.findAll();
        //遍历所有权限
        perms.forEach(p->{
            /*
            * 配置了自定义权限过滤器的话,必须改key值
            * <entry key="aisellPerms" value-ref="aisellPermissionsAuthorizationFilter"/>
            * */
            map.put(p.getUrl(),"aisellPerms["+p.getSn()+"]");
        });
        //拦截所有,登录成功后才可访问
        map.put("/**","authc");
        return map;
    }
}

1.1.2 当前用户赋权

PermissionRepository通过用户id获取权限

public interface PermissionRepository extends BaseRepository<Permission,Long>{
    /*
    * 通过用户获取对应权限
    *    返回的是一个Set<String>权限集合
    *    JPQL关连法则: 1.关连前面的类的别名.属性 2.不需要你消除笛卡尔积
    * */
    @Query("select p.sn from Employee e join e.roles r join r.permissions p where e.id = ?1")
    Set<String> findSnByUser(Long userid);
}

IPermissionService定义findSnByUser方法

public interface IPermissionService extends IBaseService<Permission,Long>{
    Set<String> findSnByUser();
}

PermissionServiceImpl重写findSnByUser方法

  • 先获取当前用户(主体里面存了)
  • 通过当前用户的id查询用户权限信息
@Service
public class PermissionServiceImpl extends BaseServiceImpl<Permission,Long> implements IPermissionService{
    @Autowired
    private PermissionRepository permissionRepository;

    @Override
    public Set<String> findSnByUser() {

        //获取当前用户主体
        Employee loginUser = UserContext.getUser();
        return permissionRepository.findSnByUser(loginUser.getId());
    }
}

AisellRealm中获取并设置权限

@Autowired
private IEmployeeService employeeService;
@Autowired
private IPermissionService permissionService;
/*
* 授权(之后的权限数据获取都在这里面)
* */
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //创建授权对象
    SimpleAuthorizationInfo authorizationInfo= new SimpleAuthorizationInfo();
    //获取角色并设置角色
    /*Set<String> roles = findRoles();
    authorizationInfo.setRoles(roles);*/
    //获取权限并设置权限(从数据库中查询该用户所有权限)
    Set<String> permissions = permissionService.findSnByUser();
    authorizationInfo.setStringPermissions(permissions);
    return authorizationInfo;
}

二.ajax请求权限拦截

2.1 自定义权限

  • 创建一个过滤器AisellPermissionsAuthorizationFilter 继承PermissionsAuthorizationFilter
  • 重写onAccessDenied方法(只有当你没有权限的时候才会进入这个方法)
/*
* 自定义权限过滤器  继承PermissionsAuthorizationFilter类
* */
public class AisellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
    /*
    * 只有当你没有权限的时候才会进入这个方法
    * */
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = this.getSubject(request, response);
        if (subject.getPrincipal() == null) {
            this.saveRequestAndRedirectToLogin(request, response);
        } else {
            //1.转换成Http协议的对象
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            //设置响应头为json
            resp.setContentType("application/json;charset=UTF-8");
            //2.获取请求头: XMLHttpRequest
            String xhr = req.getHeader("X-Requested-With");
            if("XMLHttpRequest".equals(xhr)){
                //3.代表现在是Ajax请求,我们得做ajax处理
                // 返回一个json:{success:false,msg:xxx}
                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 权限配置

    <!-- shiro真正的权限过滤器 注意:这个id必须和shiro过滤器的名称相同-->
    <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="com.luo.aisell.web.shiro.AisellPermissionsAuthorizationFilter"/>

2.3 权限使用

必须使用先前配置文件中配置的名称aisellPerms,不然找不到

public class FilterChainDefinitionMapFactory {
    @Autowired
    private IPermissionService permissionService;
    /**
     * 这个方法就会返回咱们的权限数据(它是有顺序)
     */
    public Map<String,String> creatMap(){
        //注意:这里的map是有顺序的
        Map<String,String> map = new LinkedHashMap<>();
        ...
        //获取所有权限,拦截真实权限
        List<Permission> perms = permissionService.findAll();
        //遍历所有权限
        perms.forEach(p->{
            /*
            * 配置了自定义权限过滤器的话,必须改key值
            * <entry key="aisellPerms" value-ref="aisellPermissionsAuthorizationFilter"/>
            * */
            map.put(p.getUrl(),"aisellPerms["+p.getSn()+"]");
        });
        //拦截所有,登录成功后才可访问
        map.put("/**","authc");
        return map;
    }
}

三.shiro标签认识

3.1 引入标签

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

3.2 拿到主体的标签

<shiro:principal property="username" />  

3.3 权限判断的标签

判断是否有employee:delete权限,有权限就显示删除链接

<shiro:hasPermission name="employee:delete">
    <a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</shiro:hasPermission>

四.菜单管理(根据权限显示菜单)

3.1 菜单对象

完成菜单的基础CRUD后,配置菜单类关系

  • 多对一,通过子菜单拿父菜单
  • @Transient(临时属性JPA不会管理它)一对多,不需要JPA管理(因为会拿到所有多方),得我们自己添加数据
  • @JsonIgnore双向关连生成json会死循环,需要一方放弃生成
  • getText方法 兼容easyui,前台格式name对应text
@Entity
@Table(name = "menu")
public class Menu extends BaseDomain{
    private String name;
    private String url;
    private String icon;
    
    //必须儿子可以找父亲 多对一
    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonIgnore//忽略
    private Menu parent;
    /**
     * 不要配置单向一对多(性能不好)
     * 你现在敢配置一对多嘛?配置了一对多,关系则由JPA来管理!
     * 它会自动从1方去拿到所有多方!!!
     *   1.这里是否需要儿子的List字段 -》 一定要的
     *   2.这个儿子的List是否要JPA管理 -》 不可以
     *   加上这个字段(属性),但是不交给JPA管理【咱们手动管理这个字段】
     */
    @Transient//临时,有些字段不需要和数据库有关系,我
    private List<Menu> children = new ArrayList<>();

    //兼容easyui,显示树菜单
    public String getText() {
        return name;
    }

	//getter.setter.toString...
}

3.2 获取父菜单

3.2.1 MenuRepository

  • findParentMenus:拿到所有父菜单(url为null的是父菜单)
  • findMenusByUser:通过用户拿到菜单,这里为List注意去重
public interface MenuRepository extends BaseRepository<Menu,Long>{
    //通过用户拿到菜单 这里为List注意去重
    @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);
    /*
    * url为null的是父菜单
    * */
    @Query("select o from Menu o where o.url is null")
    List<Menu> findParentMenus();
}

3.2.2 service与controller的支持

IMenuService定义获取所有父菜单的方法

public interface IMenuService extends IBaseService<Menu,Long>{
    List<Menu> findParentMenus();
}

MenuServiceImpl重写findParentMenus方法

  • 创建一个装父菜单的容器
  • 获取当前用户,通过用户获取权限菜单
  • 遍历当前用户的菜单
  • 通过子菜单获取父菜单,判断父菜单容器里是否已经存在该父菜单
    • 没有的话,把它放进去
  • 将该子菜单添加到父菜单的children中
@Service
public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService{
    @Autowired
    private MenuRepository menuRepository;

    @Override
    public List<Menu> findParentMenus() {
        //创建一个装父菜单的容器
        List<Menu> parentMenus = new ArrayList<>();
        //获取当前用户
        Employee loginUser = UserContext.getUser();
        System.out.println(loginUser);
        //通过用户获取权限
        System.out.println(loginUser.getId());
        List<Menu> menus = menuRepository.findMenusByUser(loginUser.getId());
        System.out.println(menus);
        //遍历当前用户的菜单
        menus.forEach(m->{
            //通过子菜单找父菜单
            Menu parentMenu = m.getParent();
            //判断父菜单容器里是否已经存在该父菜单
            if(!parentMenus.contains(parentMenu)){
                //没有的话,把它放进去
                parentMenus.add(parentMenu);
            }
            //将该子菜单添加到父菜单的children中
            parentMenu.getChildren().add(m);
        });

        return parentMenus;
    }
}

controller层

@RequestMapping("/findParentMenus")
@ResponseBody
public List<Menu> findParentMenus(){
    return menuService.findParentMenus();
}

3.2.3 主页面进行访问

$("#menu").tree({
    method:'get',
    url:'/menu/findParentMenus'
    ...
})

原理分析图:

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值