zoukankan      html  css  js  c++  java
  • SpringBoot集成Shiro

    一、Shiro 简介

    Apache Shiro 是一个强大且易用的Java安全框架,能够用于身份验证、授权、加密和会话管理。

    官网:https://shiro.apache.org/

    Shiro 功能:

    • 核心功能:

      • Authentication(认证):用户登录,身份识别。
      • Authorization(授权):授权和鉴权,处理用户和访问的目标资源之间的权限。
      • Session Management(会话管理):即 Session 的管理。
      • Cryptography(加密):用户密码加密。
    • 其他功能:

      • Web支持:可以非常容易集成到web应用程序中。
      • 缓存:缓存是Apache Shiro API中的第一级,以确保安全操作保持快速和高效。
      • 并发性:多线程环境完成认证和授权。
      • 测试:存在测试支持,可帮助您编写单元测试和集成测试,并确保代码按预期得到保障。
      • 运行方式:允许用户承担另一个用户的身份(如果允许)的功能,有时在管理方案中很有用。
      • 记住我:记住用户在会话中的身份,所以用户只需要强制登录即可。

    Shiro 核心对象:

    • Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户等和软件交互的任何对象。
    • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 框架的核心。
    • Realms:用于认证和授权,提供扩展点,使用者自行实现认证逻辑和授权逻辑。

    二、SpringBoot集成Shiro

    1:引入pom

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.1</version>
    </dependency>
    

    在GroupId为 org.apache.shiro下的ArtifaceId有好几个shiro-spring;shiro-springboot;shiro-springboot-starter。暂不清楚这三个之间的区别和联系,本文中引用的是 shiro-spring,使用的是当前(2021-07)最新版本 1.7.1。

    2:增加配置类
    配置类有两个:

    • 新建一个类 ShiroRealm ,此类继承 AuthorizingRealm ,是框架提供的扩展口,使用者通过重写doGetAuthenticationInfo 方法检验用户登录信息是否正确,并将用户信息存放到 session 中,此方法会抛出 AuthenticationException 异常,需要在统一异常处理中捕获此异常。通过重写 doGetAuthorizationInfo 方法为当前请求的用户赋予角色和权限信息,配合Shiro 提供的 @RequiresRoles 和 @RequiresPermissions 注解完成鉴权。

    • 新建一个类 ShiroConfig ,此类需要添加 @Configuration 注解,此类的作用是向应用程序上下文(俗称的容器)注入使用者添加的shiro配置和自定义功能。

      • 注入 SecurityManager,此对象是Shiro的核心对象之一,Shiro 框架提供了多种 DefaultSecurityManger可供使用,暂不清楚他们之间的区别,本文使用的是 DefaultWebSecurityManager 。通过 setRealm 将上一步新建的认证和授权配置类注入 SecurityManager。

      • 注入 ShiroFilterFactoryBean,此对象是一个过滤器,作用是配置一些默认页面和过滤规则。setLoginUrl是配置认证失败,默认要重定向的页面,可以是一个jsp页面,也可以是一个RESTFul接口。setFilterChainDefinitionMap是配置认证过滤规则,比如哪些URL不需要认证,接收一个 LinkedHashMap ,key为 url ,value 为认证策略,这里必须要吐槽认证策略没有设计成一个枚举。

        • logout:配置退出登录过滤器,其中的具体的退出代码Shiro已经替我们实现了,调用此接口后,页面会重定向到setLoginUrl配置的URL。
        • authc:配置需要认证的URL
        • anon:配置不需要认证的URL
      • 注入 authorizationAttributeSourceAdvisor 和 defaultAdvisorAutoProxyCreator ,如果不注入这两个对象,RequiresRoles 和RequiresPermissions 注解将无法使用。

    ShiroRealm:

    package com.naylor.shiro.config;
    
    
    import com.alibaba.fastjson.JSON;
    import com.naylor.shiro.dto.UserInfo;
    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.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    /**
    * @ClassName ShiroRealm
    * @类描述 realm(领域、范围)不太清楚这里用这个单词是什么寓意。此类继承 AuthorizingRealm ,是框架给使用者留下的两个扩展点,doGetAuthenticationInfo 扩展登录认证的逻辑;doGetAuthorizationInfo 扩展授权鉴权的逻辑
    *
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 9:28
    * @Version 1.0.0
    **/
    public class ShiroRealm extends AuthorizingRealm {
    
    
        /**
         * 授权
         * 在访问接口前,为当前登录用户赋予角色和权限
         * 实际应用中从数据库中查询用户拥有的角色和权限信息
         *
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            String principal = JSON.toJSONString(principalCollection);
            System.out.println(principal);
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            UserInfo userInfo = (UserInfo)principalCollection.getPrimaryPrincipal();
           //根据用户名查询出用户角色和权限,并交给shiro管理。实际应用中用户角色和权限从数据库获取
            if (userInfo.getUserName().equals("cml")) {
                simpleAuthorizationInfo = buildUserCmlRolePermission();
            } else if (userInfo.getUserName().equals("admin")) {
                simpleAuthorizationInfo = buildUserAdminRolePermission();
            } else if (userInfo.getUserName().equals("hn")) {
                simpleAuthorizationInfo = buildUserHnRolePermission();
            }
            return simpleAuthorizationInfo;
        }
    
    
        /**
         * 登录认证
         * 保存用户信息到session中
         * 在调用登录接口后会进入到此方法(/common/singin)
         *
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            String authToken = JSON.toJSONString(authenticationToken);
            System.out.println("authToken:" + authToken);
            String userName = authenticationToken.getPrincipal().toString();
            UserInfo userInfo = this.getUserInfoByUserName(userName);
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userInfo, userInfo.getPassword(), getName());
            return simpleAuthenticationInfo;
        }
    
    
        /**
         * 构造用户名为admin的用户的角色和权限
         * 实际应用中用户角色权限信息从数据库中获取
         * @return
         */
        private SimpleAuthorizationInfo buildUserAdminRolePermission() {
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            List<String> roles = new ArrayList<>();
            roles.add("roleA");
            roles.add("roleB");
            roles.add("roleC");
            simpleAuthorizationInfo.addRoles(roles);
            List<String> permissions = new ArrayList<>();
            permissions.add("permissionsA");
            permissions.add("permissionsB");
            permissions.add("permissionsC");
            simpleAuthorizationInfo.addStringPermissions(permissions);
            return simpleAuthorizationInfo;
        }
    
    
        /**
         * 构造用户名为cml的用户的角色和权限
         * 实际应用中用户角色权限信息从数据库中获取
         * @return
         */
        private SimpleAuthorizationInfo buildUserCmlRolePermission() {
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            List<String> roles = new ArrayList<>();
            roles.add("roleA");
            roles.add("roleB");
            simpleAuthorizationInfo.addRoles(roles);
            List<String> permissions = new ArrayList<>();
            permissions.add("permissionsA");
            permissions.add("permissionsB");
            simpleAuthorizationInfo.addStringPermissions(permissions);
            return simpleAuthorizationInfo;
        }
    
    
        /**
         * 构造用户名为hn的用户的角色和权限
         * 实际应用中用户角色权限信息从数据库中获取
         * @return
         */
        private SimpleAuthorizationInfo buildUserHnRolePermission() {
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            List<String> roles = new ArrayList<>();
            roles.add("roleA");
            List<String> permissions = new ArrayList<>();
            permissions.add("permissionsAA");
            simpleAuthorizationInfo.addStringPermissions(permissions);
            simpleAuthorizationInfo.addRoles(roles);
            return simpleAuthorizationInfo;
        }
    
    
        /**
         * 获取用户信息根据用户名
         * 实际应用场景中是从数据库查询用户信息并根据需求组装 userInfo 对象
         *
         * @param userName
         * @return
         */
        private UserInfo getUserInfoByUserName(String userName) {
            UserInfo userInfo = new UserInfo().setId("112233445566778899").setUserName(userName).setRealName("陈明亮").setUserType(5).setNation("中国").setPassword("123456");
            return userInfo;
        }
    }
    

    ShiroConfig:

    package com.naylor.shiro.config;
    
    
    import org.apache.shiro.authc.Authenticator;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    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.mgt.WebSecurityManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
    
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    
    /**
    * @ClassName ShiroConfgi
    * @类描述 Shiro 配置
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 9:24
    * @Version 1.0.0
    **/
    
    
    @Configuration
    public class ShiroConfig {
    
    
    
    
        /**
         * 注入安全管理
         * 为shiro框架核心对象,可注入不同的SecurityNamager对象,另外可根据实际需求通过securityManager的set方法自定义安全管理对象
         * @return
         */
        @Bean(name = "securityManager")
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(buildShiroRealm());
            return securityManager;
        }
    
    
        /**
         * 注入认证、授权
         * @return
         */
        @Bean(name = "shiroRealm")
        public ShiroRealm buildShiroRealm() {
            ShiroRealm shiroRealm = new ShiroRealm();
            return shiroRealm;
        }
    
    
        /**
         * 注入过滤器
         * 通过setLoginUrl配置认证失败,重定向的uri地址,可以是一个页面,也可以是一个RESTFul接口
         *
         * @param securityManager
         * @return
         */
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            Map<String, String> map = new HashMap<>();
            //退出登录
            map.put("/logout", "logout");
            //对所有URI认证
            map.put("/**", "authc");
            // 设置不用认证的URI
            map.put("/common/login", "anon");
            map.put("/common/singin", "anon");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    
    
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            //认证失败重定向URI
            shiroFilterFactoryBean.setLoginUrl("/common/login");
    
    
            return shiroFilterFactoryBean;
        }
    
    
        /**
         * 加入注解的使用,不加入这个注解不生效
         *
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
    
        /**
         * 加入注解的使用,不加入这个注解不生效
         *
         * @return
         */
        @Bean
        @ConditionalOnMissingBean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
            defaultAAP.setProxyTargetClass(true);
            return defaultAAP;
        }
    
    
    
    
    }
    

    3:增加全局异常处理
    捕获异常,防止将tomcat的错误页面直接抛给用户。

    配置文件中增加以下配置:
    出现错误时, 直接抛出异常。这两个配置是为了让404异常正常抛出

    spring.mvc.throw-exception-if-no-handler-found=true
    spring.resources.add-mappings=false
    

    GlobalException

    package com.naylor.shiro.handler;
    
    
    import com.naylor.shiro.dto.GlobalResponseEntity;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authz.AuthorizationException;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.servlet.NoHandlerFoundException;
    
    
    /**
    * @ClassName GlobalException
    * @类描述
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 14:23
    * @Version 1.0.0
    **/
    
    
    //@RestControllerAdvice("com.naylor")
    @RestControllerAdvice()
    @ResponseBody
    @Slf4j
    public class GlobalException {
    
    
    
    
        /**
         * 处理511异常
         * @param e
         * @return
         */
        @ExceptionHandler(AuthenticationException.class)
        public ResponseEntity<Object> handleAuthenticationException(AuthenticationException e) {
            return new ResponseEntity<>(
                    new GlobalResponseEntity<>(false, "511",
                            e.getMessage() == null ? "认证失败" : e.getMessage()),
                    HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
        }
    
    
    
    
        /**
         * 处理401异常
         * @param e
         * @return
         */
        @ExceptionHandler(AuthorizationException.class)
        public ResponseEntity<Object> handleAuthorizationException(AuthorizationException e) {
            return new ResponseEntity<>(
                    new GlobalResponseEntity<>(false, "401",
                            e.getMessage() == null ? "未授权" : e.getMessage()),
                    HttpStatus.UNAUTHORIZED);
        }
    
    
    
    
        /**
         * 处理404异常
         *
         * @return
         */
        @ExceptionHandler(NoHandlerFoundException.class)
        public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException e) {
            return new ResponseEntity<>(
                    new GlobalResponseEntity(false, "404",
                            e.getMessage() == null ? "请求的资源不存在" : e.getMessage()),
                    HttpStatus.NOT_FOUND);
        }
    
    
        /**
         * 捕获运行时异常
         *
         * @param e
         * @return
         */
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<Object> handleRuntimeException(RuntimeException e) {
            log.error("handleRuntimeException:", e);
            return new ResponseEntity<>(
                    new GlobalResponseEntity(false, "500",
                            e.getMessage() == null ? "运行时异常" : e.getMessage().replace("java.lang.RuntimeException: ", "")),
                    HttpStatus.INTERNAL_SERVER_ERROR);
        }
    
    
        /**
         * 捕获一般异常
         * 捕获未知异常
         *
         * @param e
         * @return
         */
        @ExceptionHandler(Exception.class)
        public ResponseEntity<Object> handleException(Exception e) {
            return new ResponseEntity<>(
                    new GlobalResponseEntity<>(false, "555",
                            e.getMessage() == null ? "未知异常" : e.getMessage()),
                    HttpStatus.INTERNAL_SERVER_ERROR);
        }
    
    
    }
    

    4:增加统一的RESTFul响应结构体
    GlobalResponse:

    package com.naylor.shiro.handler;
    
    
    import com.alibaba.fastjson.JSON;
    import com.naylor.shiro.dto.GlobalResponseEntity;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    
    import javax.annotation.Resource;
    
    
    /**
    * @ClassName GlobalResponse
    * @类描述
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 14:22
    * @Version 1.0.0
    **/
    
    
    @RestControllerAdvice("com.naylor")
    public class GlobalResponse  implements ResponseBodyAdvice<Object> {
    
    
        /**
         * 拦截之前业务处理,请求先到supports再到beforeBodyWrite
         * <p>
         * 用法1:自定义是否拦截。若方法名称(或者其他维度的信息)在指定的常量范围之内,则不拦截。
         *
         * @param methodParameter
         * @param aClass
         * @return 返回true会执行拦截;返回false不执行拦截
         */
        @Override
        public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
            //TODO 过滤
            return true;
        }
    
    
        /**
         * 向客户端返回响应信息之前的业务逻辑处理
         * <p>
         * 用法1:无论controller返回什么类型的数据,在写入客户端响应之前统一包装,客户端永远接收到的是约定的格式
         * <p>
         * 用法2:在写入客户端响应之前统一加密
         *
         * @param responseObject     响应内容
         * @param methodParameter
         * @param mediaType
         * @param aClass
         * @param serverHttpRequest
         * @param serverHttpResponse
         * @return
         */
        @Override
        public Object beforeBodyWrite(Object responseObject, MethodParameter methodParameter,
                                      MediaType mediaType,
                                      Class<? extends HttpMessageConverter<?>> aClass,
                                      ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            //responseObject是否为null
            if (null == responseObject) {
                return new GlobalResponseEntity<>("55555", "response is empty.");
            }
            //responseObject是否是文件
            if (responseObject instanceof Resource) {
                return responseObject;
            }
            //该方法返回值类型是否是void
            //if ("void".equals(methodParameter.getParameterType().getName())) {
            //  return new GlobalResponseEntity<>("55555", "response is empty.");
            //}
            if (methodParameter.getMethod().getReturnType().isAssignableFrom(Void.TYPE)) {
                return new GlobalResponseEntity<>("55555", "response is empty.");
            }
            //该方法返回值类型是否是GlobalResponseEntity。若是直接返回,无需再包装一层
            if (responseObject instanceof GlobalResponseEntity) {
                return responseObject;
            }
            //处理string类型的返回值
            //当返回类型是String时,用的是StringHttpMessageConverter转换器,无法转换为Json格式
            //必须在方法体上标注RequestMapping(produces = "application/json; charset=UTF-8")
            if (responseObject instanceof String) {
                String responseString = JSON.toJSONString(new GlobalResponseEntity<>(responseObject));
                return responseString;
            }
            //该方法返回的媒体类型是否是application/json。若不是,直接返回响应内容
            if (!mediaType.includes(MediaType.APPLICATION_JSON)) {
                return responseObject;
            }
    
    
            return new GlobalResponseEntity<>(responseObject);
        }
    }
    

    5:用户登录认证
    用户信息都存放在 Subject 对象中,用户登录认证的过程只需调用其 login 方法即可,login方法内部会调用 doGetAuthenticationInfo 扩展点完成登录的认证。

    package com.naylor.shiro.controller;
    
    
    import com.naylor.shiro.dto.User;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.springframework.web.bind.annotation.*;
    
    
    /**
    * @ClassName LoginController
    * @类描述
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 9:51
    * @Version 1.0.0
    **/
    
    
    @RestController
    @RequestMapping("/common")
    public class CommonController {
    
    
        /**
         * 提示需要登录
         * @return
         */
        @GetMapping(value = "/login")
        public String login() {
            return "请登录";
        }
    
    
        /**
         * 登录
         * @param user
         * @return
         */
        @PostMapping("/singin")
        public String singIn(@RequestBody User user) {
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(), user.getPassword());
            SecurityUtils.getSubject().login(usernamePasswordToken);
            return "登录成功";
        }
    
    
    
    
    
    
        @GetMapping("/error")
        public String error() {
            return "500";
        }
    }
    

    6:用户请求鉴权
    通过 @RequiresRoles 和 @RequiresPermissions 注解的配合使用,完成对后端接口的鉴权。鉴权的逻辑其实就是从Shiro 中取出当前用户拥有的角色和权限,然后和RESTFul接口上面注解的角色和权限进行对比,如果包含那么就鉴权通过,允许访问,否则就抛出401异常。

    鉴权原理:
    debug 到AuthorizingRealm类的 isPermitted 方法,该方法接收两个参数,Permission为RESTFul接口上面添加的权限相关注解,AuthorizationInfo是当前请求用户拥有的角色和权限。鉴权的原理就是判断AuthorizationInfo 中是否包含Permission。

    获取用户信息和session信息:
    通过 SecurityUtils 工具类中的 getSubject方法获取用户的登录信息和sessionId

    package com.naylor.shiro.controller;
    
    
    import com.naylor.shiro.dto.UserInfo;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    /**
    * @ClassName AnimalController
    * @类描述
    * @Author MingliangChen
    * @Email cnaylor@163.com
    * @Date 2021-07-08 13:49
    * @Version 1.0.0
    **/
    
    
    @RestController
    @RequestMapping("/animal")
    public class AnimalController {
    
    
    
    
        @GetMapping("/cat")
        public String cat() {
            Subject subject = SecurityUtils.getSubject();
            UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
            String sessionId = String.valueOf(SecurityUtils.getSubject().getSession().getId());
            return "cat";
        }
    
    
        @RequiresRoles({"roleA"})
        @RequiresPermissions("permissionsAA")
        @GetMapping("/fish")
        public String fish() {
            return "fish";
        }
    
    
        @RequiresRoles({"roleA", "roleB"})
        @GetMapping("/dog")
        public String dog() {
            return "dog";
        }
    
    
        @RequiresPermissions("permissionsC")
        @GetMapping("/tiger")
        public String tiger() {
            Boolean a = SecurityUtils.getSubject().hasRole("roleC");
            Boolean b = SecurityUtils.getSubject().isPermitted("permissionsC");
            return "tiger";
        }
    }
    

    7:测试
    使用postman模拟用户请求进行测试。

    a. 在没有登录的情况下,调用任何接口都会重定向到 /common/login 接口,该接口返回“请登录”。即使是访问一个不存在的页面也会重定向,因为我们在ShiroConfig 中配置的是全局认证。

    b.调用登录接口登录,注意用户名需要和代码中写死的用户名一致

    c.调用受限接口

    admin 用户有 tiger 接口的权限,调用之后接口正常返回 tiger ; 没有 fish 接口的权限,调用之后返回 “Subject does not have permission [permissionsAA]”

    8:总结
    本文演示了SpringBoot集成 Shiro ,基于 Session 来管理用户会话,实现用户和web服务的认证和鉴权。Shiro 作为一个古老的框架,历史悠久,功能和拓展性也特别的强,如使用者可以自定义 SessionMode=HTTP 从而可以达到web服务横向扩容的目的;也可以结合 JWT 搭建无状态的web服务;还可以搭建 oauth2 。但是后两者并不推荐,在分布式系统和微服务应用中,推荐使用SpringBootSecutiryOauth2来搭建自己的授权服务。

    邮箱:cnaylor@163.com
    技术交流QQ群:1158377441
  • 相关阅读:
    json字符串与java对象的相互转换(jackson)
    EventBus的使用
    android 定时任务
    使用FlaycoBanner实现图片轮播效果(加载网络图片)
    okHttp使用
    Android 终于官方支持按百分比来设置控件的宽高了
    Android下使用Properties文件保存程序设置
    google vr开源 cardboard
    Android开源控件PhotoView的使用
    Android 布局优化
  • 原文地址:https://www.cnblogs.com/Naylor/p/14990965.html
Copyright © 2011-2022 走看看