wisdomsell-day7(智能商贸系统第七天)
1.权限判断
权限的拦截也应该是从数据库中获取
public class FilterChainDefinitionMapFactory{
@Autowired
private IPermissionService permissionService;
//添加权限拦截数据(应该从数据库中获取)
List<Permission> perms = permissionService.findAll();
perms.forEach(p->{
//perms:PermissionsAuthorizationFilter
//aisellPerms:AiSellPermissionsAuthorizationFilter
map.put(p.getUrl(),"perms["+p.getSn()+"]");
});
}
1.1从数据库中获取用户的权限
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);
}
1.2拿到当前用户的对象
public class UserContext {
/**
*
* 拿到当前登陆用户
* 封装成一个类,后面有重复得直接调用就行
这里通过主体来获取,只要得到主体,就可以在任意地方获取对象
*/
public static Employee getUser(){
Subject subject = SecurityUtils.getSubject();
Employee loginUser = (Employee)subject.getPrincipal();
return loginUser;
}
}
1.3完成service层里面代码
public interface IPermissionService extends IBaseService<Permission,Long> {
//根据当前用户获取对应权限
Set<String> findSnByUser();
}
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());
}
}
2.权限的ajax问题
我们在发送请求时有两种请求,一种是ajax请求,另外一种是普通请求,对于普通请求,没有权限的处理的方式就是跳转到一个页面,然后显示消息,而ajax请求对于没有权限的处理直接就在当前页面显示就可以
2.1用自定义拦截器
通过自定义拦截器就可以即保留普通请求,通过也能保留ajax请求
/**
* 自定义一个拦截器,现在发送请求一个是ajax请求,另外一个是普通的请求
* 普通请求是跳转到页面,这就导致在员工页面进行删除操作时,如果没有该权限,就会出错,现在我们自己配置一个拦截器,如果没有该权限
* 根据发送的请求不同,进入不同的操作,ajax请求和普通请求最主要的区别有无X-Requested-With
* 这里本来应该继承PermissionsAuthorizationFilter的父类AuthorizationFilter
* 但是为了保留PermissionsAuthorizationFilter里面的功能,所以继承PermissionsAuthorizationFilter
* 但是实现的方法还是用的父类的方法
*/
public class MyPermissionsAuthorizationFilter 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;
//2.获取到头,根据头来进行判断
String header = req.getHeader("X-Requested-With");
//3.处理成为ajax数据
if("XMLHttpRequest".equals(header)){
resp.setContentType("application/json;charset=UTF-8");
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中
<property name="filters">
<map>
<entry key="myperms" value-ref="myPermissionsAuthorizationFilter"></entry>
</map>
</property>
<bean id="myPermissionsAuthorizationFilter" class="com.yyk.aisell.web.shiro.MyPermissionsAuthorizationFilter"></bean>
//将权限拦截的名字改为自定义拦截的名字
map.put(p.getUrl(),"perms["+p.getSn()+"]");
//改为
map.put(p.getUrl(),"myperms["+p.getSn()+"]");
3.配置菜单的crud
和前面的权限以及角色一样,复制然后替换为相对应的字段
4.配置一对多
这里我们选择通过父菜单去找子菜单,配置为一对多关系,这样右边可以显示数据库中的菜单,但是是没有层级的,如果想配置为有层级关系的,配置一对多在这里就会出现问题。因为jsp在处理通过一对多去找子菜单时,会将它的所有子类菜单都找出来,这样是不符合要求的,所以这里不能配置一对多关系,而应该配置为多对一,但是应该保留一对多的字段
/**
* 这里如果一开始配置一对多关系,即通过父类去找到子类菜单,是没有问题的,但是前台展示就没有层级关系,如果你要有层级关系
* jpa在通过父类去寻找子类时,是把所有的子类找出来,这样是不行的,但是这个字段是一定要保留的
* 那么只能通过子类去寻找父类,配置一对多的关系
* @Transient临时的,让jpa不要去管他,以后如果有必须要存在的字段,但是数据库中没有该字段,就通过这个注解解决
*/
@Transient
private List<Menu> children = new ArrayList<>();
@ManyToOne
@JoinColumn(name="parent_id"
private Menu parent;
4.1完成service层代码
接口
List<Menu> findParentMenus();
实现类
public class MenuServiceImpl extends BaseServiceImpl<Menu,Long> implements IMenuService {
@Autowired
private MenuRepository menuRepository;
@Override
public List<Menu> findParentMenus() {
//1.准备一个空的父菜单容器
List<Menu> parentMenus = new ArrayList<>();
//2.拿到用户
Employee loginUser = UserContext.getUser();
//3.拿到这个用户的菜单
List<Menu> menus = menuRepository.findMenusByUser(loginUser.getId());
//4.遍历这个子菜单
menus.forEach(m->{
//通过子菜单去获得父菜单
Menu parentMenu = m.getParent();
//进行判断,如果容器里面没有该父菜单,进放入该父菜单,如果有,就不要管他
if(!parentMenus.contains(parentMenu)){
parentMenus.add(parentMenu);
}
//将子菜单也要放入到该容器中,因为现在该容器只有父菜单
parentMenu.getChildren().add(m);
});
return menuRepository.findParentMenus();
}
}
4.2完成dao层里面代码
public interface MenuRepository extends BaseRepository<Menu,Long> {
//连接数据库中的菜单
@Query("select o from Menu o where o.url is null ")
List<Menu> findParentMenus();
//
@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);
}
4.3完成permission权限里面的多对一关联
//配置与菜单的外键关系
@ManyToOne
@JoinColumn(name="menu_id")
private Menu menu;
5.解决json数据量过大问题(死循环)
在menu类中加入注解 @JsonIgnore
/**
JsonIgnore:SpringMVC传json就不会使用这个字段了
*/
@ManyToOne
@JoinColumn(name="parent_id")
@JsonIgnore
private Menu parent;
6.通过引入标签的形式来将用户没有的权限隐藏
在role角色jsp页面中
<%--引入通过标签去隐藏没有的权限,即登陆的时候不显示没有的权限--%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasPermission name="employee:delete">
<a href="#" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</shiro:hasPermission>