aisell项目Day07_细节

(一)主要实现功能

① 实现修改session中的主体为当前对象
② 实现授权管理
③ 实现菜单栏从数据库中显现

(二)细节处理

(1)从数据库获取权限

1、将权限变为从数据库中获取的真实权限
在拦截代码中:
1)注入权限业务层(因为在shiro的xml中配置了bean )
2)从数据库中获取所有的权限
3)将获取的权限集合遍历,并将每一个权限存入自定义拦截代码中(注意此时需要通过字符串,拼接编号)
在这里插入图片描述

(2)session处理

  1. 登录成功后主体为用户
    以前登录成功,传如session中的是username,现在传主体Employee对象,在开发中也大多传入的是对象
//解决身份认证(登录)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取令牌
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        //2.根据令牌,获取用户名
        String username = token.getUsername();
        Employee employee = employeeService.findByUsername(username);
        //4.数据库中无法根据用户传入的用户名查询到对应的对象,即对象为null,则代表该用户不存在
        if(employee==null){
            //为null,代表用户名不存在,shior自动抛出UnknownAccountException
            return null;
        }
        //在这里加盐值需一个ByteSource对象,而Shiro提供了一个ByteSource对象
        ByteSource salt = ByteSource.Util.bytes("lzj");
        /**登录验证的用户身份信息,传入数据,shiro会自动比较获取的密码与令牌中用户传入的密码
         *              如果对应不上,则会自动抛出IncorrectCredentialsException异常
         * 第一个参数:存入的是当前主体,shiro可以在项目的任何地方获取当前的主体(在开发中一般使用直接存放当前的主体)
         * 第二个参数:存入的是当前用户的密码
         * 第三个参数:存入的是盐值
         * 第四个参数:存入的是自定义取值方法名(名字随意,只需保存唯一即可)
         */
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());

        return authenticationInfo;
    }
  1. shiro的主体

此处的当事人principle,可以随意,因为此用户是主体(登录之后存入的东西,shiro是可以在任何地方都可以获取)如果当前的主体是当前对象的话,就会报错,不能在权限验证时将eployee转为一个String=====》将此处修改为对象(开发的时候,大多数用的是对象。存对象)

在这里插入图片描述

修改前台,直接获取属性
在这里插入图片描述

  1. shiro的会话管理
    sbject不仅仅是当前用户,还是一个容器,还装有session(会话管理)、principal(主体)等等
    注意此时的session不是http内的session。
    shiro会自动同步代理,转为http的session
    很多地方会使用当前用户—》抽取公共类
    //存入上下文中的当前对象
    

public class UserContext {
/此处字符串内设置为userInSession,因为ajax不支持有"_"的变量定义方式/
private static final String USER_IN_SESSION = “userInSession”;

public static void setUser(Employee loginUser) {
    Subject subject = SecurityUtils.getSubject();
    subject.getSession().setAttribute(USER_IN_SESSION, loginUser);
}

public static Employee getUser() {
    Subject subject = SecurityUtils.getSubject();
    return (Employee) subject.getSession().getAttribute(USER_IN_SESSION);
}

}
````

(3)授权管理

权限是从数据库中获取,前台应该显示当前用户的所有权限

  • PermissionRepository写完JPQL之后,业务层进行调用

JPQL关联原则: 1.不写on 2.关联对象的别名.属性

//根据用户拿到他对应的所有权限
@Query("select distinct p.sn from Employee e join e.roles r join r.permissions p where e.id = ?1")
Set<String> findPermsByUser(Long userId);
  • AiSell 获取当前用户的所有权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    Employee loginUser = UserContext.getUser();
   //根据当前用户拿到对应的权限
    Set<String> perms = permissionService.findPermsByUser(loginUser.getId());
    //准备并返回AuthorizationInfo这个对象
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.setStringPermissions(perms);
    return authorizationInfo;
}

(4)Ajax请求权限处理

shiro处理没有权限是跳转页面,而我们如果是ajax请求,我们希望是返回json数据 ajax请求会有一个请求头:X-Requested-With: XMLHttpRequest 需要自定义一个shiro的权限过滤器

  • 自定义权限过滤器
//自定义shiro的过滤器,单独处理ajax请求
public class AiSellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    @Override
    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 httpRequest = (HttpServletRequest) request;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            //2.有MLHttpRequest对象,则为ajax请求
            String xRequestedWith = httpRequest.getHeader("X-Requested-With");
            if (xRequestedWith != null &&"XMLHttpRequest".equals(xRequestedWith)) {
                //3.在这里就代表是ajax请求
                //表示ajax请求 {"success":false,"message":"没有权限"}
                httpResponse.setContentType("text/json; charset=UTF-8");
                httpResponse.getWriter().print("{\"success\":false,\"msg\":\"对不起,您无权操作此功能\"}");
            }else {
                String unauthorizedUrl = this.getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    WebUtils.toHttp(response).sendError(401);
                }
            }

        }
        return false;
    }
}

  • 让Spring管理自定义过滤器,并告诉shiro使用自定义过滤器,在applicationContext-shiro.xml内配置
<!--引入操作拦截或方形的路径和页面的javaBean-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMapBuilderMap"/>
        <property name="filters">
            <map>
                <entry key="aisellPerm" value-ref="AiSellPermissionsAuthorizationFilter"></entry>
            </map>
        </property>
<!--自定义过滤器-->
    <bean id="AiSellPermissionsAuthorizationFilter" class="cn.lzj.aisell.shiro.AiSellPermissionsAuthorizationFilter"/>

@Autowired
private IPermissionService permissionService;
public Map<String,String> createFilterChainDefinitionMap(){
    ...
    //拿到所有权限
    List<Permission> perms = permissionService.findAll();
    //设置相应的权限
    perms.forEach(p -> {
        filterChainDefinitionMap.put(p.getUrl(), "aisellPerms["+p.getSn()+"]");
    });
    ...
}

(5)菜单管理

  • Menu

菜单domain的自关连配置
需要配置双向,但是不能让JPA去管理一对多(我们自己管理:@Transient)
双向生成JSON会产生死循环,需要一边进行忽略:@JsonIgnore

//让它不再生成JSON
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    @JsonIgnore
    private Menu parent;

    // 临时属性 -> 这个字段JPA就不管它了
    @Transient
    private List<Menu> children = new ArrayList<>();
  • MenuRepository
public interface MenuRepository 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> findByUser(Long userId);
}
  • MenuServiceImpl

根据设计只能通过员工找到子菜单
需要通过子菜单拿到父菜单
判断这个父菜单是否已经存到集合中
如果这个菜单单没有存起来,放到集合中 把当前这个子菜单放到父菜单中去

@Override
    public List<Menu> findMenus() {
        //1.父菜单容器
        List<Menu> parents = new ArrayList<>();
        //2.获取当事人的对应的所有子菜单
        Employee user = UserContext.getUser();
        List<Menu> menus = menuRepository.findMenusByLoginUser(user.getId());
        //3.遍历子菜单,获取父菜单,并且放入parents父容器中  --》此处只能使用for循环,不能使用for-each
        for(int i=0;i<menus.size();i++){
            //3.1 通过子菜单获取其对应的父菜单
            Menu childrenMenu = menus.get(i);
            Menu menuParent = childrenMenu.getParent();
            //3.2 将父菜单放入父菜单容器中(避免重复,需要判断该父菜单在容器内是否存在)
            if(!parents.contains(menuParent)){
                parents.add(menuParent);
            }
            //4.将子菜单放入父菜单中
            menuParent.getChildren().add(childrenMenu);
        }
        return parents;
    }
  • UtilController中返回值
@Autowired
private IMenuService menuService;

@RequestMapping("/loginUserMenu")
@ResponseBody
public List<Menu> loginUserMenu(){
    return menuService.findLoginMenu();
}
  • main.jsp修改路径
 $('#menuTree').tree({
                url:'/util/loginUserMenus',
  • shiro:hasPermission
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
...
<shiro:hasPermission name="employee:delete">
    <a href="javascript:;" data-method="delete" class="easyui-linkbutton" iconCls="icon-remove" plain="true">删除</a>
</shiro:hasPermission>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值