• Springboot+Shiro+Jwt实现权限管理


    Springboot+Shiro+Jwt实现权限管理

    简要的说明下我们为什么要用JWT,因为我们要实现完全的前后端分离,所以不可能使用session,cookie的方式进行鉴权,所以JWT就被派上了用场,你可以通过一个加密密钥来进行前后端的鉴权,实现无状态鉴权。

    之前我一直使用spring security,相比较而言security的功能更强大但是很笨重,shiro功能简单更轻量级。反正各有利弊,应该根据公司现有的技术体系(尽量和公司其他系统框架一致以免浪费很多口舌)。

    认证原理

    • 用户登陆之后,使用密码对账号进行签名生成并返回token并设置过期时间;
    • 将token保存到本地,并且每次发送请求时都在header上携带token。
    • shiro过滤器拦截到请求并获取header中的token,并提交到自定义realm的doGetAuthenticationInfo方法。
    • 通过jwt解码获取token中的用户名,从数据库中查询到密码之后根据密码生成jwt效验器并对token进行验证。

    设计用户权限表

    这里我就是简单的用户、角色、权限的表,可以根据自己的需求来即可。当然也可以没有表,用户、角色、权限去会员中心或统一登录中心去获取,只需要把我后面代码中查询本地数据库的改为调用接口即可。

    • 用户表
    CREATE TABLE `sys_user` (
      `id` varchar(255) COLLATE utf8_bin NOT NULL,
      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `create_time` datetime(6) DEFAULT NULL,
      `del_flag` int(11) DEFAULT NULL,
      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `update_time` datetime(6) DEFAULT NULL,
      `avatar` varchar(1000) COLLATE utf8_bin DEFAULT NULL,
      `dept_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `description` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `last_login_time` datetime(6) DEFAULT NULL,
      `mobile` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `nick_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `password` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `sex` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `status` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `username` varchar(255) COLLATE utf8_bin NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `UK_pcxnofptxqnyi4wqa3v5lbu21` (`username`),
      KEY `idx_deptId` (`dept_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
    
    • 角色表
    CREATE TABLE `sys_role` (
      `id` varchar(255) COLLATE utf8_bin NOT NULL,
      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `create_time` datetime(6) DEFAULT NULL,
      `del_flag` int(11) DEFAULT NULL,
      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `update_time` datetime(6) DEFAULT NULL,
      `remark` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `role_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
    
    • 权限表(我这里对应的就是菜单,按你自己的需求来。)
    CREATE TABLE `sys_menu` (
      `id` varchar(255) COLLATE utf8_bin NOT NULL,
      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `create_time` datetime(6) DEFAULT NULL,
      `del_flag` int(11) DEFAULT NULL,
      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `update_time` datetime(6) DEFAULT NULL,
      `component` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `icon` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `menu_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `order_num` double DEFAULT NULL,
      `parent_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `path` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `perms` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `type` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT;
    
    • 用户VS角色表
    CREATE TABLE `sys_user_role` (
      `id` varchar(255) COLLATE utf8_bin NOT NULL,
      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `create_time` datetime(6) DEFAULT NULL,
      `del_flag` int(11) DEFAULT NULL,
      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `update_time` datetime(6) DEFAULT NULL,
      `role_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `user_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_userId_roleId` (`user_id`,`role_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
    
    
    • 角色VS权限表
    CREATE TABLE `sys_role_menu` (
      `id` varchar(255) COLLATE utf8_bin NOT NULL,
      `create_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `create_time` datetime(6) DEFAULT NULL,
      `del_flag` int(11) DEFAULT NULL,
      `update_by` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `update_time` datetime(6) DEFAULT NULL,
      `menu_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      `role_id` varchar(255) COLLATE utf8_bin DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_roleId_menuId` (`role_id`,`menu_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
    
    

    引入相关pom依赖

    基于spring-boot-starter-web,如何创建自行百度。

    <!-- shiro-spring -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.0</version>
    </dependency>
    
    <!-- jwt -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.4.1</version>
    </dependency>
    

    配置JWT

    • 自定义JwtToken,首先我们需要自定义一个对象用来封装token。

    JWTToken差不多就是Shiro用户名密码的载体。因为我们是前后端分离,服务器无需保存用户状态,所以不需要RememberMe这类功能,我们简单的实现下AuthenticationToken接口即可。因为token自己已经包含了用户名等信息,所以这里我就弄了一个字段。如果你喜欢钻研,可以看看官方的UsernamePasswordToken是如何实现的。

    package cn.pconline.config.auth;
    
    import lombok.Data;
    import org.apache.shiro.authc.AuthenticationToken;
    
    /**
     * @Description JwtToken 传输类
     * @Author jie.zhao
     * @Date 2019/8/7 10:45
     */
    @Data
    public class JwtToken implements AuthenticationToken {
    
        private static final long serialVersionUID = 1282057025599826155L;
    
        private String token;
    
        private String exipreAt;
    
        public JwtToken(String token) {
            this.token = token;
        }
    
        public JwtToken(String token, String exipreAt) {
            this.token = token;
            this.exipreAt = exipreAt;
        }
    
        @Override
        public Object getPrincipal() {
            return token;
        }
    
        @Override
        public Object getCredentials() {
            return token;
        }
    
    }
    
    
    • JwtUtil工具类用来进行签名和效验Token。

    我们写一个简单的JWT加密,校验工具,并且使用用户自己的密码充当加密密钥,这样保证了token 即使被他人截获也无法破解。并且我们在token中附带了username信息,并且设置密钥30分钟就会过期。

    package cn.pconline.config.auth;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTDecodeException;
    import com.auth0.jwt.exceptions.JWTVerificationException;
    import com.auth0.jwt.interfaces.DecodedJWT;
    
    import java.util.Date;
    
    /**
     * @Description Jwt工具类
     * @Author jie.zhao
     * @Date 2019/8/7 10:59
     */
    public class JwtUtil {
    
        /**
         * 过期时间30分钟,这里需要根据具体的需求来
         */
        private static final long EXPIRE_TIME = 30 * 60 * 1000;
    
        /**
         * 校验token是否正确
         *
         * @param token    密钥
         * @param username 用户名
         * @param secret   用户的密码
         * @return 正确: true;不正确:false
         */
        public static boolean verify(String token, String username, String secret) {
            // 根据密码生成JWT校验器
            try {
                Algorithm algorithm = Algorithm.HMAC256(secret);
                JWTVerifier verifier = JWT.require(algorithm)
                        .withClaim("username", username)
                        .build();
                // 校验TOKEN
                DecodedJWT jwt = verifier.verify(token);
                return true;
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                return false;
            } catch (JWTVerificationException e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 获取用户名
         *
         * @param token token中包含了用户名
         * @return
         */
        public static String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * 生成签名
         *
         * @param username 用户名
         * @param secret   密码
         * @return 加密的TOKEN
         */
        public static String sign(String username, String secret) {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带用户信息
            return JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
        }
    }
    
    

    配置Shiro

    • 自定义Realm

    realm的用于处理用户是否合法的这一块,需要我们自己实现。

    package cn.pconline.config.auth;
    
    import cn.pconline.modules.sys.entity.SysUser;
    import cn.pconline.modules.sys.service.SysUserService;
    import org.apache.commons.lang3.StringUtils;
    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 org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.Set;
    
    /**
     * @Description  自定义实现 ShiroRealm,包含认证和授权两大模块
     * @Author jie.zhao
     * @Date 2019/8/13 11:19 
     */
    public class MyRealm extends AuthorizingRealm {
    
        @Autowired
        private SysUserService userService;
    
        /**
         * 必须重写此方法,不然Shiro会报错
         */
        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof JwtToken;
        }
    
        /**
         * 授权模块,获取用户角色和权限
         * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String username = JwtUtil.getUsername(principals.toString());
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            // 获取用户角色集
            Set<String> roleSet = userService.getUserRoles(username);
            simpleAuthorizationInfo.setRoles(roleSet);
            // 获取用户权限集
            Set<String> permissionSet = userService.getUserPermissions(username);
            simpleAuthorizationInfo.setStringPermissions(permissionSet);
            return simpleAuthorizationInfo;
        }
    
        /**
         * 用户认证
         * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
         * @param authenticationToken 身份认证 token
         * @return AuthenticationInfo 身份认证信息
         * @throws AuthenticationException 认证相关异常
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            // 这里的 token是从 JwtFilter 的 executeLogin 方法传递过来的,已经经过了解密
            String token = (String) authenticationToken.getCredentials();
            String username = JwtUtil.getUsername(token);
            if (StringUtils.isBlank(username)) {
                throw new AuthenticationException("token校验不通过");
            }
            // 如果要实现登出逻辑需要将用户和token存储起来(redis、memcache等)这里校验token是否有效
            
            // 通过用户名查询用户信息,也可改为接口验证用户名是否存在(即通过登录中心验证的)
            SysUser user = userService.getUser(username);
            if (user == null) {
                throw new AuthenticationException("用户名或密码错误");
            }
            /*
             * 注意这里的校验
             * token
             * username 用户名
             * secret 用户的密码
             *
             * 这里要注意secret这个字段,如果本地系统没有用户存储用户密码(即通过登录中心验证的)
             * 可以把这个值写成一个固定值,当然这样有一定的风险,或者根据一定的规则生成假的密码来验证。
             *
             */
            if (!JwtUtil.verify(token, username, user.getPassword())) {
                throw new AuthenticationException("token校验不通过");
            }
            return new SimpleAuthenticationInfo(token, token, "my_realm");
        }
    }
     
    
    
    • 配置ShiroFilter拦截器

    所有的请求都会先经过Filter,所以我们继承官方的BasicHttpAuthenticationFilter,并且重写鉴权的方法。
    代码的执行流程preHandle->isAccessAllowed->isLoginAttempt->executeLogin

    package cn.pconline.config.auth;
    
    import cn.pconline.config.authentication.JwtToken;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * @Description Jwt过滤器
     * @Author jie.zhao
     * @Date 2019/8/7 10:47
     */
    @Slf4j
    public class JwtFilter extends BasicHttpAuthenticationFilter {
    
        private static final String TOKEN = "Authentication";
    
        /**
         * 判断用户是否想要登入。
         * 检测header里面是否包含Authorization字段即可
         */
        @Override
        protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
            HttpServletRequest req = (HttpServletRequest) request;
            String authorization = req.getHeader(TOKEN);
            return authorization != null;
        }
    
        /**
         *
         */
        @Override
        protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String authorization = httpServletRequest.getHeader(TOKEN);
    
            JwtToken token = new JwtToken(authorization);
            // 提交给realm进行登入,如果错误他会抛出异常并被捕获
            getSubject(request, response).login(token);
            // 如果没有抛出异常则代表登入成功,返回true
            return true;
        }
    
        /**
         * 这里我们详细说明下为什么最终返回的都是true,即允许访问
         * 例如我们提供一个地址 GET /article
         * 登入用户和游客看到的内容是不同的
         * 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西
         * 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入
         * 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可
         * 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大
         */
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            if (isLoginAttempt(request, response)) {
                try {
                    executeLogin(request, response);
                } catch (Exception e) {
                    response401(request, response);
                }
            }
            return true;
        }
    
        /**
         * 对跨域提供支持
         */
        @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);
        }
    
        /**
         * 将非法请求跳转到 /401
         */
        private void response401(ServletRequest req, ServletResponse resp) {
            try {
                HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
                httpServletResponse.sendRedirect("/401");
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    }
    
    
    • ShiroConfig
    package cn.pconline.config.auth;
    
    import cn.pconline.config.authentication.JwtFilter;
    import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
    import org.apache.shiro.mgt.DefaultSubjectDAO;
    import org.apache.shiro.mgt.SecurityManager;
    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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.servlet.Filter;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * @Description Shiro 配置类
     * @Author jie.zhao
     * @Date 2019/8/7 11:02
     */
    @Configuration
    public class ShiroConfig {
    
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 配置 SecurityManager,并注入 shiroRealm
            securityManager.setRealm(myRealm());
    
            /*
             * 关闭shiro自带的session,详情见文档
             * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
             */
            DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
            DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
            defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
            subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
            securityManager.setSubjectDAO(subjectDAO);
            return securityManager;
        }
    
        @Bean
        public MyRealm myRealm() {
            // 配置 Realm
            return new MyRealm();
        }
    
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    
            // 添加自己的过滤器并且取名为jwt
            LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
            filters.put("jwt", new JwtFilter());
            factoryBean.setFilters(filters);
    
            factoryBean.setSecurityManager(securityManager);
            factoryBean.setUnauthorizedUrl("/401");
    
            /*
             * 自定义url规则
             * http://shiro.apache.org/web.html#urls-
             */
            Map<String, String> filterRuleMap = new HashMap<>();
            // 所有请求通过我们自己的JWT Filter
            filterRuleMap.put("/**", "jwt");
            // 访问401和404页面不通过我们的Filter
            filterRuleMap.put("/401", "anon");
            factoryBean.setFilterChainDefinitionMap(filterRuleMap);
            return factoryBean;
        }
    
        /**
         * 下面的代码是添加注解支持
         */
        @Bean
        @DependsOn("lifecycleBeanPostProcessor")
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            // 强制使用cglib,防止重复代理和可能引起代理出错的问题
            // https://zhuanlan.zhihu.com/p/29161098
            defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
            return defaultAdvisorAutoProxyCreator;
        }
    
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    }
    
    

    简单的登录控制器

    对于登出逻辑,其实是不好实现的因为JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

    这里我是通过将用户+token存储在redis中,登录时存储起来,在再MyRealm实现token有效性的校验。该逻辑未实现请自己根据自己的业务实现。

    以下是伪代码,可根据自己的业务自己实现。

    /login
    登入
    
    /article
    所有人都可以访问,但是用户与游客看到的内容不同
    
    /require_auth
    登入的用户才可以进行访问
    
    /require_role
    admin的角色用户才可以登入
    
    /require_permission
    拥有view和edit权限的用户才可以访问 
    
    
    @RestController
    public class WebController {
    
        private static final Logger LOGGER = LogManager.getLogger(WebController.class);
    
        private UserService userService;
    
        @Autowired
        public void setService(UserService userService) {
            this.userService = userService;
        }
    
        @PostMapping("/login")
        public ResponseBean login(@RequestParam("username") String username,
                                  @RequestParam("password") String password) {
             // 通过用户名查询用户信息,也可改为接口验证用户名是否存在(即通过登录中心验证的) 
            UserBean userBean = userService.getUser(username);
            if (userBean.getPassword().equals(password)) {
                return new ResponseBean(200, "Login success", JWTUtil.sign(username, password));
            } else {
                throw new UnauthorizedException();
            }
        }
    
        @GetMapping("/article")
        public ResponseBean article() {
            Subject subject = SecurityUtils.getSubject();
            if (subject.isAuthenticated()) {
                return new ResponseBean(200, "You are already logged in", null);
            } else {
                return new ResponseBean(200, "You are guest", null);
            }
        }
    
        @GetMapping("/require_auth")
        @RequiresAuthentication
        public ResponseBean requireAuth() {
            return new ResponseBean(200, "You are authenticated", null);
        }
    
        @GetMapping("/require_role")
        @RequiresRoles("admin")
        public ResponseBean requireRole() {
            return new ResponseBean(200, "You are visiting require_role", null);
        }
    
        @GetMapping("/require_permission")
        @RequiresPermissions(logical = Logical.AND, value = {"view", "edit"})
        public ResponseBean requirePermission() {
            return new ResponseBean(200, "You are visiting permission require edit,view", null);
        }
    
        @RequestMapping(path = "/401")
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public ResponseBean unauthorized() {
            return new ResponseBean(401, "Unauthorized", null);
        }
    } 
     
    

    参考文档:

    https://www.itwork.club/2018/10/08/springboot-shiro-jwt/

    https://juejin.im/post/59f1b2766fb9a0450e755993

    -------------已经触及底线 感谢您的阅读-------------
  • 相关阅读:
    OP和DBA相关的一些有用资源
    对于有大量重复数据的表添加唯一索引
    Innodb_io_capacity 对于IO稳定性的一些研究
    Memcache Slab Eviction 功能测试
    MMM的一个Bug
    阿里嘉年华ADC Workshop PPT分享
    Java中的死锁问题
    Java中的线程同步
    Java中终止正在运行线程
    Java中的线程的优先级
  • 原文地址:https://www.cnblogs.com/cnsyear/p/12777991.html
Copyright © 2020-2023  润新知