aisell07 权限与菜单

一. 权限管理

1.1 权限获取

	之前我们一直在模拟数据库中的数据
	现在我们直接去数据库获取真实权限数据
public class FilterChainDefinitionMapFactory {
    /**
     * 返回权限数据
     */

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> creatMap(){
        //准备一个有序的Map   LinkedHashMap
        Map<String,String> map = new LinkedHashMap<>();
        ///添加放选择数据
        map.put("/login","anon");
        //把所有的静态资源(js,css,图片,...)放行
        map.put("*.js","anon");
        map.put("*.css","anon");
        map.put("/login/**","anon");
        map.put("/css/**","anon");
        map.put("/js/**","anon");
        map.put("/easyui/**","anon");
        map.put("/images/**","anon");
        //添加权限拦截数据
        /*map.put("/employee/index","perms[employee:index]");
        map.put("/department/index","perms[department:index]");*/
        //添加权限拦截数据
        List<Permission> perms = permissionService.findAll();
        perms.forEach(p->{
            //aisellPerms: 知道找自己定义的过滤器
            map.put(p.getUrl(),"aisellPerms["+p.getSn()+"]");
        });
        //拦截所有
        map.put("/**","authc");
        return map;
    }
}

1.2 当前用户权限

	1. PermissionRepositroy 这里添加一个方法
	   自己写JPQL获取
public interface PermissionRepositroy extends BaseRepository<Permission,Long> {
    //根据用户获取权限
    @Query("select p.sn from Employee e join e.roles r join r.permissions p where e.id=?1")
    Set<String> findSnByUser(Long userId);
}
	2. 业务层 IPermissionService 添加获取当前用户权限方法
public interface IPermissionService extends IBaseService<Permission,Long>{
    /*
    * 扩展
     */
    //根据当前用户获取对应的权限
    Set<String> findSnByUser();
}
	3. PermissionServiceImpl 实现
@Service
public class PermissionServiceImpl extends BaseServiceImpl<Permission,Long> implements IPermissionService{

    @Autowired
    private PermissionRepositroy permissionRepositroy;

    @Override
    public Set<String> findSnByUser() {
        Employee loginUser = UserContext.getUser();
        return permissionRepositroy.findSnByUser(loginUser.getId());
    }
}
	4. AisellRealm 获取权限
public class AisellRealm extends AuthorizingRealm{

        @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> perms = permissionService.findSnByUser();
            authorizationInfo.setStringPermissions(perms);
            return authorizationInfo;
        }
    .....
}

二. ajax 请求权限拦截

2.1 自定义权限,写一个类

	我们在删除一条数据的时候,因为用的是ajax请求,
	所以在删除失败时,错误提示是undefined
	我们需要显示正确的错误提示信息
	
	解决方案:
		自定义一个类,继承 PermissionsAuthorizationFilter
		重写 onAccessDenied
	
	细节:
		ajax的请求头有一个: X-Requested-With:XMLHttpRequest
		只需要判断请求头有没有这个参数
		协议转换成Http协议
//自定义权限过滤器
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 {
            //协议转换 Http
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            //设置响应头为json
            resp.setContentType("application/json;charset=UTF-8");

            //获取请求头 ajax ===> X-Requested-With:XMLHttpRequest
            String xhr = req.getHeader("X-Requested-With");
            //如果是ajax请求
            if ("XMLHttpRequest".equals(xhr)){
                //返回标准json格式
                resp.getWriter().print("{\"success\":false,\"msg\":\"你没有权限\"}");
            }else {
                //普通url请求,没有权限的页面
                String unauthorizedUrl = this.getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    //有路径跳到没有权限的位置
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    //没有路径报错
                    WebUtils.toHttp(response).sendError(401);
                }
            }
        }
        return false;
    }
}

2.2 权限配置 applicationContext-shiro.xml中配置

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
	......
	<property name="filters">
            <map>
                <!-- aisellPerms 知道找这个过滤器 -->
                <entry key="aisellPerms" value-ref="aisellFilter"/>
            </map>
        </property>
</bean>
  !--配置自己的过滤器  -->
  <bean id="aisellFilter" class="com.yangrui.aisell.web.shiro.AiSellPermissionsAuthorizationFilter"/>

2.3 权限使用

	记得要加上aisellPerms,这样才知道找我们自己的权限过滤器
public class FilterChainDefinitionMapFactory {
    /**
     * 返回权限数据
     */

    @Autowired
    private IPermissionService permissionService;
    
    ......
    
    List<Permission> perms = permissionService.findAll();
        perms.forEach(p->{
            //aisellPerms: 知道找自己定义的过滤器
            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="javascript:;" data-method="delete" class="easyui-linkbutton" data-options="iconCls:'icon-remove',plain:true">删除</a>
</shiro:hasPermission>

四. 菜单管理(不带权限)

	菜单基本结构显示

4.1 Menu对象

@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<>();
    ...
}

4.2 获取父级菜单

	获取所有父级菜单
	父级菜单没有url,根据这个入手
public interface MenuRepositroy extends BaseRepository<Menu,Long> {
    //父菜单
    @Query("select m from Menu m where m.url is null")
    List<Menu> findParentMenus();
}

4.3 IMenuService

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

4.4 MenuServiceImpl

@Service
public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService{
	@Override
	public List<Menu> findParentMenus() {
    	return menuRepository.findParentMenus();
	}
}

4.5 MenuController

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

4.6 主页面main访问

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

五. 菜单管理(带权限显示)

	每个用户登录后是根据自己的权限查找到相应的菜单

5.1 Menu对象

Entity
@Table(name = "menu")
public class Menu extends BaseDomain{
    private String name;
    private String url;
    private String icon;

    /**
     * 配置一对多,父亲菜单能够找到儿子菜单
     * 不配一对多,但是字段必须要有,不然没有树形结构
     * @Transient: 临时属性,不和数据库建立关系,不然会一直循环调用
     */
    @Transient
    private List<Menu> children = new ArrayList<>();
//--------------------------------------------------------
    /**
     * 多对一,儿子菜单可以找到父亲菜单
     * @JsonIgnore: SpringMVC传json的时候就不会传这个字段
     */
    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonIgnore
    private Menu parent;
    ...
    get set方法
}

5.2 Permission对象

@Entity
@Table(name = "permission")
public class Permission extends BaseDomain{
    private String name;
    private String url;
    private String descs;
    private String sn;

    @ManyToOne
    @JoinColumn(name = "menu_id")
    private Menu menu;
    ...
    get set方法
}

5.3 Role对象

@Entity
@Table(name = "role")
public class Role extends BaseDomain{
    private String name;
    private String sn;

    @ManyToMany
    @JoinTable(name = "role_permission",joinColumns = @JoinColumn(name = "role_id"),
                inverseJoinColumns = @JoinColumn(name = "permission_id"))
    private List<Permission> permissions = new ArrayList<>();
    ...
    get set方法
}

5.4 MenuRepositroy 增加用户菜单方法

public interface MenuRepositroy extends BaseRepository<Menu,Long> {
    //当前用户菜单
    @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);

    //父菜单
    @Query("select m from Menu m where m.url is null")
    List<Menu> findParentMenus();
}

5.5 MenuServiceImpl

	List集合装起来
@Service
public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService{

    @Autowired
    private MenuRepositroy menuRepositroy;

    @Override
    public List<Menu> findParentMenus() {
        //集合来装父菜单,子集合装子菜单
        //创建父菜单容器
        List<Menu> parentMenus = new ArrayList<>();
        //获取当前用户
        Employee loginUser = UserContext.getUser();
        //获取当前用户的菜单
        List<Menu> menus = menuRepositroy.findMenusByUser(loginUser.getId());
        //遍历子菜单
        menus.forEach(m->{
            Menu parentMenu = m.getParent();
            //判断集合中是否有父菜单
            if(!parentMenus.contains(parentMenu)){
                //如果没有,把父菜单添加进去
                parentMenus.add(parentMenu);
            }
            //添加子菜单
            parentMenu.getChildren().add(m);
        });
        return parentMenus;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值