zoukankan      html  css  js  c++  java
  • springboot整合shiro实现前后端分离项目

    Shiro 是一个强大、简单易用的 Java 安全框架,可使认证、授权、加密,会话过程更便捷,并可为应用提供安全保障。本节重点介绍下 Shiro 的认证和授权功能。

    1 Shiro 三大核心组件

    Shiro 有三大核心组件,即 Subject、SecurityManager 和 Realm。先来看一下它们之间的关系。

    在这里插入图片描述

    可以看到:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject;其每个 API 的含义:

    Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

    SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

    Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

    也就是说对于我们而言,最简单的一个 Shiro 应用:

    1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;

    2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

    从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

    接下来我们来从 Shiro 内部来看下 Shiro 的架构,如下图所示:

    Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;

    SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

    Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

    Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

    Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

    SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);

    SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;

    CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

    Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。

    2 Shiro 身份和权限认证

    2.1 Shiro 身份认证

    我们分析下 Shiro 身份认证的过程,首先看一下官方给出的认证图。

    在这里插入图片描述

    从图中可以看到,这个过程包括五步。

    Step1:应用程序代码调用 Subject.login(token) 方法后,传入代表最终用户身份的 AuthenticationToken 实例 Token。

    Step2:将 Subject 实例委托给应用程序的 SecurityManager(Shiro 的安全管理)并开始实际的认证工作。这里开始了真正的认证工作。

    Step3、4、5:SecurityManager 根据具体的 Realm 进行安全认证。从图中可以看出,Realm 可进行自定义(Custom Realm)。

    2.2 Shiro 权限认证

    权限认证,也就是访问控制,即在应用中控制谁能访问哪些资源。在权限认证中,最核心的三个要素是:权限、角色和用户。

    权限(Permission):即操作资源的权利,比如访问某个页面,以及对某个模块的数据进行添加、修改、删除、查看操作的权利。
    角色(Role):指的是用户担任的角色,一个角色可以有多个权限。
    用户(User):在 Shiro 中,代表访问系统的用户,即上面提到的 Subject 认证主体。

    它们之间的的关系可以用下图来表示:

    在这里插入图片描述

    一个用户可以有多个角色,而不同的角色可以有不同的权限,也可有相同的权限。比如说现在有三个角色,1 是普通角色,2 也是普通角色,3 是管理员,角色 1 只能查看信息,角色 2 只能添加信息,管理员对两者皆有权限,而且还可以删除信息。

    3 Spring Boot 集成 Shiro

    因为是demo,没有引入数据库表,有需要的可以自己建,也不难,就5张表:用户表、角色表、权限表、用户角色关联表、角色权限关联表。有需要的话还可以加一张部门表

    项目结构如下:

    3.1 依赖导入

    Spring Boot 2.0.4 集成 Shiro 需要导入如下 starter 依赖:

    <!--引入shiro-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring-boot-starter</artifactId>
                <version>1.5.3</version>
            </dependency>
    3.2 自定义 Realm

    自定义 Realm 需要继承 AuthorizingRealm 类,该类封装了很多方法,且继承自 Realm 类。

    继承 AuthorizingRealm 类后,我们需要重写以下两个方法。

    doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息。
    doGetAuthorizationInfo() 方法:为当前登录成功的用户授予权限和分配角色。

    具体实现如下,相关注解请见代码注释:

    package com.zdyl.springboot_shiro.shiro.realms;
    
    import com.zdyl.springboot_shiro.shiro.utils.CurrentCredentialsMatcher;
    import com.zdyl.springboot_shiro.shiro.utils.JwtToken;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.credential.CredentialsMatcher;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    /**
     * 4 自定义realm
     */
    public class CustomerRealm extends AuthorizingRealm {
    
    
        // 设置realm的名称
        @Override
        public void setName(String name) {
            super.setName("customRealm");
        }
    
        /**
         * 大坑!,必须重写此方法,不然Shiro会报错
         */
        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof JwtToken;
        }
    
        /**
         * 授权
         *
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("进来了授权");
            JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            authorizationInfo.addStringPermission("zdyl:test:1");
            return authorizationInfo;
        }
    
        /**
         * 认证
         *
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("进来了认证");
            JwtToken jwtToken = (JwtToken) authenticationToken;
            jwtToken.setUsername("1");
            String token = jwtToken.getToken();
            // 第二步:根据用户输入的userCode从数据库查询
            // ....
            // 如果查询不到返回null
            //数据库中用户账号是zhangsansan
            /*if(!userCode.equals("zhangsansan")){//
                return null;
            }*/
            // 模拟从数据库查询到密码
            String password = "111112";
            // 如果查询到返回认证信息AuthenticationInfo
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                    jwtToken, password, this.getName());
            return simpleAuthenticationInfo;
        }
    
        /**
         * 自定义密码匹配器
         * @param credentialsMatcher
         */
        @Override
        public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
            //自定义密码匹配器
            CurrentCredentialsMatcher currentCredentialsMatcher = new CurrentCredentialsMatcher();
    
            super.setCredentialsMatcher(currentCredentialsMatcher);
        }
    }

    *认证的时候需要返回SimpleAuthenticationInfo,它有3参数构造和4参数构造,第一个参数可以传用户名或者用户,主要是比较密码的时候从里面取,第二个参数传数据库查询的密码,第三个参数(如果需要传的话)传盐,至于盐是什么不知道的可以自行百度,

    第四个传realmName。

    3.3 自定义 密码匹配器

    验证密码的时候默认走的是SimpleCredentialsMatcher的doCredentialsMatch方法,是明文比较的,需要自定义的可以继承SimpleCredentialsMatcher重写doCredentialsMatch方法。

    如果需要MD5加密比较可以继承HashedCredentialsMatcher重写doCredentialsMatch方法。

    package com.zdyl.springboot_shiro.shiro.utils;
    
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
    
    /**
     * 自定义密码匹配器
     */
    public class CurrentCredentialsMatcher extends SimpleCredentialsMatcher {
    
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            JwtToken jwtToken = (JwtToken) token;
            Object tokenCredentials = jwtToken.getToken();
            Object accountCredentials = this.getCredentials(info);
            return this.equals(tokenCredentials, accountCredentials);
        }
    
    }
    3.4 Shiro 配置

    自定义 Realm 写好了,接下来需要配置 Shiro。我们主要配置三个东西:自定义 Realm、安全管理器 SecurityManager、seession管理器sessionManager和 Shiro 过滤器。

    首先,配置自定义的 Realm,代码如下:

    package com.zdyl.springboot_shiro.shiro.config;
    
    import com.zdyl.springboot_shiro.shiro.realms.CustomerRealm;
    import com.zdyl.springboot_shiro.shiro.utils.AuthFilter;
    import org.apache.shiro.realm.Realm;
    import org.apache.shiro.session.mgt.SessionManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.Filter;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * shiro配置
     */
    @Configuration
    public class ShiroConfig {
    
        //1创建shiroFilter  负责拦截请求
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //给filter设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            Map<String, Filter> filter = new HashMap<>();
            filter.put("oauth2", new AuthFilter());
         //自定义过滤器 shiroFilterFactoryBean.setFilters(filter);
    //配置系统受限资源 Map<String, String> filterMap = new LinkedHashMap<>(); // filterMap.put("/test", "anon"); filterMap.put("/**", "oauth2"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean; } //2.创建安全管理器 @Bean("securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("realm") Realm realm, SessionManager sessionManager) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); //给安全管理器设置realm defaultWebSecurityManager.setRealm(realm); //给安全管理器设置sessionManager defaultWebSecurityManager.setSessionManager(sessionManager); return defaultWebSecurityManager; } //3.创建自定义realm @Bean("realm") public Realm getRealm() { CustomerRealm customerRealm = new CustomerRealm(); return customerRealm; } //4.创建sessionManager @Bean("sessionManager") public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager(); //设置session过期时间3600s defaultWebSessionManager.setGlobalSessionTimeout(3600000L); return defaultWebSessionManager; } /** * Shiro生命周期处理器: * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm) * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager) */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 启用shrio授权注解拦截方式,AOP式方法级权限检查 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }

    配置 Shiro 过滤器时,我们引入了安全管理器。

    至此,我们可以看出,Shiro 配置一环套一环,遵循从 Reaml 到 SecurityManager 再到 Filter 的过程。在过滤器中,我们需要定义一个 shiroFactoryBean,然后将 SecurityManager 引入其中,需要配置的内容主要有以下几项。

    • 默认登录的 URL:身份认证失败会访问该 URL。
    • 认证成功之后要跳转的 URL。
    • 权限认证失败后要跳转的 URL。
    • 需要拦截或者放行的 URL:这些都放在一个 Map 中。

    通过上面的代码,我们也了解到, Map 中针对不同的 URL有不同的权限要求,下表总结了几个常用的权限。

    在这里插入图片描述

    3.5 自定义AuthenticationToken认证的时候传参用(用户名、密码、token)
    package com.zdyl.springboot_shiro.shiro.utils;
    
    import org.apache.shiro.authc.AuthenticationToken;
    
    public class JwtToken implements AuthenticationToken {
        String serialVersionUID = "8939244780389542801";
        private String token;
        private String username;
    
        public JwtToken(String token) {
            this.token = token;
        }
    
        @Override
        public Object getPrincipal() {
            return null;
        }
    
        @Override
        public Object getCredentials() {
            return null;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getToken() {
            return token;
        }
    
        public void setToken(String token) {
            this.token = token;
        }
    }
    3.6 自定义过滤器
    package com.zdyl.springboot_shiro.shiro.utils;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
    import org.apache.shiro.web.util.WebUtils;
    import org.springframework.http.HttpStatus;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * 自定义过滤器
     */
    public class AuthFilter extends AuthenticatingFilter {
    
    
        /**
         * 获取token
         *
         * @param servletRequest
         * @param servletResponse
         * @return
         * @throws Exception
         */
        @Override
        protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
            String requestToken = getRequestToken((HttpServletRequest) servletRequest);
            JwtToken jwtToken = new JwtToken(requestToken);
            return jwtToken;
        }
    
        /**
         * 对跨域提供支持
         *
         * @param request
         * @param response
         * @return
         * @throws Exception
         */
        @Override
        protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
            if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
                httpServletResponse.setStatus(HttpStatus.OK.value());
                return false;
            }
            return super.preHandle(request, response);
        }
    
        /**
         * 验证token
         * 当访问拒绝时是否已经处理了;
         * 如果返回true表示需要继续处理;
         * 如果返回false表示该拦截器实例已经处理完成了,将直接返回即可。
         * @param servletRequest
         * @param servletResponse
         * @return
         * @throws Exception
         */
        @Override
        protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
            //完成token登入
            //1.检查请求头中是否含有token
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String token = getRequestToken(httpServletRequest);
            //2. 如果客户端没有携带token,拦下请求
            if (null == token || "".equals(token)) {
                responseTokenError(servletResponse, "Token为空,您无权访问该接口");
                return false;
            }
            //3. 如果有,对进行进行token验证
            return executeLogin(servletRequest, servletResponse);
        }
    
        /**
         * 执行认证
         *
         * @param request
         * @param response
         * @return
         * @throws Exception
         */
        @Override
        protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
            JwtToken jwtToken = (JwtToken) createToken(request, response);
            try {
                SecurityUtils.getSubject().login(jwtToken);
            } catch (AuthenticationException e) {
                responseTokenError(response, "Token无效,您无权访问该接口");
                return false;
            }
            return true;
        }
    
    
        /**
         * 获取请求的token
         */
        private String getRequestToken(HttpServletRequest httpRequest) {
            //从header中获取token
            String token = httpRequest.getHeader("token");
    
            //如果header中不存在token,则从参数中获取token
            if (StringUtils.isEmpty(token)) {
                token = httpRequest.getParameter("token");
            }
            if (StringUtils.isEmpty(token)) {
                Cookie[] cks = httpRequest.getCookies();
                if (cks != null) {
                    for (Cookie cookie : cks) {
                        if (cookie.getName().equals("yzjjwt")) {
                            token = cookie.getValue();
                            return token;
                        }
                    }
                }
            }
            return token;
        }
    
        /**
         * 无需转发,直接返回Response信息 Token认证错误
         */
        private void responseTokenError(ServletResponse response, String msg) {
            HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
            httpServletResponse.setStatus(HttpStatus.OK.value());
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");
            try (PrintWriter out = httpServletResponse.getWriter()) {
                ObjectMapper objectMapper = new ObjectMapper();
                String data = objectMapper.writeValueAsString(new ResponseBean(401, msg, null));
                out.append(data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    3.7 自定义返回数据类
    package com.zdyl.springboot_shiro.shiro.utils;
    
    import lombok.Data;
    
    /**
     * 返回数据
     */
    @Data
    public class ResponseBean {
    
        /**
         * 200:操作成功  -1:操作失败
         **/
    
        // http 状态码
        private int code;
    
        // 返回信息
        private String msg;
    
        // 返回的数据
        private Object data;
    
        public ResponseBean() {
        }
    
        public ResponseBean(int code, String msg, Object data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public static ResponseBean error(String message) {
            ResponseBean responseBean = new ResponseBean();
            responseBean.setMsg(message);
            responseBean.setCode(-1);
            return responseBean;
        }
    
        public static ResponseBean error(int code, String message) {
            ResponseBean responseBean = new ResponseBean();
            responseBean.setMsg(message);
            responseBean.setCode(code);
            return responseBean;
        }
    
        public static ResponseBean success(Object data) {
            ResponseBean responseBean = new ResponseBean();
            responseBean.setData(data);
            responseBean.setCode(200);
            responseBean.setMsg("成功");
            return responseBean;
        }
    
        public static ResponseBean success(String message) {
            ResponseBean responseBean = new ResponseBean();
            responseBean.setData(null);
            responseBean.setCode(200);
            responseBean.setMsg(message);
            return responseBean;
        }
    
        public static ResponseBean success() {
            ResponseBean responseBean = new ResponseBean();
            responseBean.setData(null);
            responseBean.setCode(200);
            responseBean.setMsg("Success");
            return responseBean;
        }
    }
    3.8 使用 Shiro 进行认证

    至此,我们完成了 Shiro 的准备工作。接下来开始使用 Shiro 进行认证。

    使用 http://localhost:8080/test进行身份认证。

    请求的时候请求头携带token

    @RestController
    public class TestController {
    
        @RequestMapping("/test")
        public void test() {
            System.out.println("555555555555");
        }
    }

    我们重点分析下用户带token访问。整个处理过程是这样的。

    首先,根据前端传来的用户名和密码,创建一个 Token。

    然后,请求头携带token访问 http://localhost:8080/test。首先被自定义的过滤器拦截,验证token。

    紧接着,调用 subject.login(token) 进行身份认证——注意,这里传入了刚刚创建的 Token,如注释所述,这一步会跳转入自定义的 Realm,访问 doGetAuthenticationInfo 方法,开始身份认证。

    最后,启动项目,测试一下。在浏览器中请求:http://localhost:8080/test, 首先进行身份认证,此时token不正确或者为空,会返回401提示token为空或者不正确。

    3.9 使用 Shiro 进行授权

    使用 http://localhost:8080/test进行授权。在需要授权的接口是上增加@RequiresPermissions("zdyl:test:1")

    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
    
        @RequiresPermissions("zdyl:test:1")
        @RequestMapping("/test")
        public void test() {
            System.out.println("555555555555");
        }
    }

    我们重点分析下授权的过程。整个处理过程是这样的。

    用户通过了认证环节,请求的资源也就是接口上有@RequiresPermissions("zdyl:test:1")这个注解,就会进到自定义realm中执行doGetAuthorizationInfo方法,该方法有一个入参PrincipalCollection,通俗点说就是用户信息(用户名啥的 ),就是认证最后一步返回的SimpleAuthenticationInfo里的第一个参数。通过用户信息,从数据库获取用户的角色列表和权限列表放进SimpleAuthorizationInfo,最后返回SimpleAuthorizationInfo,后面的工作shiro就帮我们干了。权限列表里如果包含注解上的zdyl:test:1,就可以正常访问,如果不

    包含就会报没有访问权限的错误。

  • 相关阅读:
    [转]进程间通信----pipe和fifo
    [转]udev
    [转]netlink
    [转]进程间通信-----管道
    [转]socket
    [转]armv8 memory system
    [转]内核态和用户态
    [转]dpdk内存管理
    meeting and robert rules
    notion
  • 原文地址:https://www.cnblogs.com/wiliamzhao/p/13415939.html
Copyright © 2011-2022 走看看