shiro基本概念,自定义过滤器实现前后端分离以及衍生的动态菜单的制作

不用shiro实现用户的认证,授权,也可以用springmvc的拦截器来实现,参考下列文章的前半部分
参考文献

基本概念

1.3.1 Subject

Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

1.3.2 SecurityManager

SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

1.3.3 Authenticator

Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

1.3.4 Authorizer

Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

1.3.5 realm

Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

1.3.6 sessionManager

sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

1.3.7 SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

1.3.8 CacheManager

CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

1.3.9 Cryptography

Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

拦截器的种类:
在这里插入图片描述

自定义过滤器:

原理:自己写一个类继承原来有的拦截器,然后再@Bean注入这个拦截器,
最后在配置类里面加一句:

  // 自定义过滤器

    *Map<String, Filter> filterMap = shiroFilterFactoryBean.getFilters();
    filterMap.put("hasToken", accessTokenFilter());
    shiroFilterFactoryBean.setFilters(filterMap);*

accessTokenFilter()就是一个得到自定义拦截器的方法

@Bean
public AccessTokenFilter accessTokenFilter(){
    return new AccessTokenFilter();
}

shiro前后端分离需要自定义拦截器

几乎所有的shiro教程都是用Jsp写的,那么用ajax请求进行前后端分离时是无法使用shiro的,当session失效,或者用户的资源权限不足都会重定向到一个接口,但是ajax基本不会处理重定向,都会报错;
解决方案:修改重定向302为正常返回200

当用户没有登录或session失效后,是使用FormAuthenticationFilter进行拦截处理的,我们可以重写里面的onAccessDenied方法,从而修改重定向。

public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse) response;
            if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
                resp.setStatus(HttpStatus.OK.value());
                return true;
            }

            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
			//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
            String ajaxHeader = req.getHeader(ShiroConstant.USERID);
            if (ajaxHeader != null || req.getHeader("xx") != null) {
                //前端Ajax请求,则不会重定向
                resp.setHeader("Access-Control-Allow-Origin",  req.getHeader("Origin"));
                resp.setHeader("Access-Control-Allow-Credentials", "true");
                resp.setContentType("application/json; charset=utf-8");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter out = resp.getWriter();
                JSONObject result = new JSONObject();
                result.put("message", "请重新登录!");
                result.put("statusCode", -401);
                out.println(result);
                out.flush();
                out.close();
            } else {
                saveRequestAndRedirectToLogin(request, response);
            }
            return false;
        }
    }
}

同理,用户权限不足是使用AuthorizationFilter进行拦截处理的,我们依然可以重写里面的onAccessDenied方法,如下:

public class ShiroRoleAuthorizationFilter extends AuthorizationFilter {
    
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
            resp.setStatus(HttpStatus.OK.value());
            return true;
        }
		//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
        String ajaxHeader = req.getHeader(ShiroConstant.USERID);
        if (ajaxHeader != null || req.getHeader("xx") != null) {
            //前端Ajax请求,则不会重定向
            resp.setHeader("Access-Control-Allow-Origin",  req.getHeader("Origin"));
            resp.setHeader("Access-Control-Allow-Credentials", "true");
            resp.setContentType("application/json; charset=utf-8");
            resp.setCharacterEncoding("UTF-8");
            PrintWriter out = resp.getWriter();
            JSONObject result = new JSONObject();
            result.put("message", "权限不足!");
            result.put("statusCode", -403);
            out.println(result);
            out.flush();
            out.close();
            return false;
        }
        return super.onAccessDenied(request, response);
    }
}

这样,前端Ajax请求被Shiro拦截后就不会出现返回302重定向的问题了。参考文献

动态菜单的制作

思路:
动态菜单:系统里面有很多菜单可以进行点击操作,但是不同的角色用户能够操做的菜单肯定是不会相同的,那么,我就需要用角色来区分用户,进而区分用户所能看到/操作的菜单

每个菜单我给其定义一个url,通过该url访问对应的菜单,将这个url和角色进行绑定,然后角色和用户进行绑定,属于同一角色下的用户也就拥有该角色下绑定的url

数据表:
这里有五张表,也就是上面说的菜单表、角色表、用户表、角色菜单关联表和角色用户管理表
通过上面五张表的设计及其关联,可以在用户登录的时候,根据该用户所属的角色,在role_menu表里找出所有的菜单的实体集合,然后放在session里面,登录跳转后从Session里读出菜单集合,这样就可以实现动态菜单,每个角色下的菜单操作权限也就实现了。

具体的每个菜单下的小权限,比如按钮、下拉框、搜索框之类的权限操作,可以读取菜单的url的session后通过Js进行这些操作表单的隐藏或者显示,综上,动态菜单和菜单下内容的具体小权限操作都能实现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值