• SpringBoot笔记:SpringBoot集成JWT、Shiro实战


    前言

    代码库:https://gitee.com/leo825/springboot-learning-parents.git
    之前写过《Spring集成Shiro框架实战》,最近看了JWT,正好两个整合一下,加深一下理解。

    实战演练

    代码结构

    代码结构

    SQL脚本

    schema.sql

    create table if not exists t_user (
    	`id`            int  primary  key not null ,
    	`username`      char (50) not null,
    	`password`      char(50) not null,
    	`role`          char(50) not null,
    	`permission`    char(255) not null
    ) ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    data.sql

    insert into t_user(id, username, password, role, permission) values (0,'smith','smith123','user','view');
    insert into t_user(id, username, password, role, permission) values (1,'danny','danny123','admin','view,edit')
    
    • 1
    • 2

    数据格式如下:

    usernamepasswordrolepermission
    smithsmith123userview
    dannydanny123adminview,edit

    pom依赖

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    		 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0modelVersion>
    	<parent>
    		<groupId>org.examplegroupId>
    		<artifactId>springboot-learning-parentsartifactId>
    		<version>1.0-SNAPSHOTversion>
    	parent>
    
    	<groupId>springboot-demogroupId>
    	<artifactId>springboot-jwt-shiroartifactId>
    	<version>1.0-SNAPSHOTversion>
    	<name>springboot-jwt-shironame>
    	<url>https://gitee.com/leo825/springboot-learning-parents.giturl>
    	<description>springboot集成jwt和shiro测试description>
    	<properties>
    		<start-class>com.demo.SpringbootJwtShiroApplicationstart-class>
    		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    		<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
    		<java.version>1.8java.version>
    		<mybatisplus.version>3.5.1mybatisplus.version>
    		<freemaker.version>2.3.31freemaker.version>
    		<mysql.version>8.0.28mysql.version>
    	properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-devtoolsartifactId>
    		dependency>
    
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-webartifactId>
    		dependency>
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-testartifactId>
    			<scope>testscope>
    		dependency>
    		<dependency>
    			<groupId>org.projectlombokgroupId>
    			<artifactId>lombokartifactId>
    		dependency>
    
    		
    		<dependency>
    			<groupId>com.baomidougroupId>
    			<artifactId>mybatis-plus-boot-starterartifactId>
    			<version>${mybatisplus.version}version>
    		dependency>
    		<dependency>
    			<groupId>com.baomidougroupId>
    			<artifactId>mybatis-plus-generatorartifactId>
    			<version>${mybatisplus.version}version>
    		dependency>
    
    		
    		<dependency>
    			<groupId>com.h2databasegroupId>
    			<artifactId>h2artifactId>
    		dependency>
    
    		
    		<dependency>
    			<groupId>com.auth0groupId>
    			<artifactId>java-jwtartifactId>
    			<version>3.8.1version>
    		dependency>
    		<dependency>
    			<groupId>com.alibabagroupId>
    			<artifactId>fastjsonartifactId>
    			<version>1.2.47version>
    		dependency>
    
    		
    		<dependency>
    			<groupId>org.apache.shirogroupId>
    			<artifactId>shiro-spring-boot-starterartifactId>
    			<version>1.9.0version>
    		dependency>
    	dependencies>
    
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.bootgroupId>
    				<artifactId>spring-boot-maven-pluginartifactId>
    			plugin>
    		plugins>
    	build>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    User

    package com.demo.model;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 用户
     */
    @TableName("t_user")
    @Data
    public class User implements Serializable {
        private static final long serialVersionUID = 1L;
        /**
         * 用户主键
         */
        @TableId(value = "id", type = IdType.AUTO)
        private Integer id;
        /**
         * 用户名
         */
        private String username;
        /**
         * 密码
         */
        private String password;
        /**
         * 角色
         */
        private String role;
        /**
         * 权限
         */
        private String permission;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    JwtUtil

    package com.demo.util;
    
    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.interfaces.DecodedJWT;
    
    import java.util.Date;
    
    /**
     * jwt工具类
     */
    public class JwtUtil {
    
        /**
         * 过期时间5分钟
         */
        private static final long EXPIRE_TIME = 5 * 60 * 1000;
    
        /**
         * jwt 密钥
         */
        public static final String SECRET = "jwt_secret";
    
        /**
         * 校验token是否正确
         *
         * @param token  密钥
         * @param secret 用户的密码
         * @return 是否正确
         */
        public static boolean verify(String token, String username, String secret) {
            try {
                Algorithm algorithm = Algorithm.HMAC256(secret);
                JWTVerifier verifier = JWT.require(algorithm)
                        .withClaim("username", username)
                        .build();
                DecodedJWT jwt = verifier.verify(token);
                return true;
            } catch (Exception exception) {
                return false;
            }
        }
    
        /**
         * 获得token中的信息无需secret解密也能获得
         *
         * @return token中包含的用户名
         */
        public static String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                return null;
            }
        }
    
        /**
         * 生成签名,5min后过期
         *
         * @param username 用户名
         * @param pwd      用户的密码
         * @return 加密的token
         */
        public static String sign(String username, String pwd) {
            try {
                Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
                Algorithm algorithm = Algorithm.HMAC256(pwd);
                // 附带username信息
                return JWT.create()
                        .withClaim("username", username)
                        .withExpiresAt(date)
                        .sign(algorithm);
            } catch (Exception e) {
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    UserMapper

    package com.demo.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.demo.model.User;
    
    /**
     * 用户表
     */
    public interface UserMapper extends BaseMapper<User> {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    UserService

    public interface UserService extends IService<User> {
        User getUser(String username) throws AuthenticationException;
    }
    
    • 1
    • 2
    • 3

    UserServiceImpl

    package com.demo.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.demo.mapper.UserMapper;
    import com.demo.model.User;
    import com.demo.service.UserService;
    import org.springframework.stereotype.Service;
    import javax.security.sasl.AuthenticationException;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
        @Override
        public User getUser(String username) throws AuthenticationException {
            Map<String,Object> columnMap = new HashMap<>();
            columnMap.put("username", username);
            List<User> users = baseMapper.selectByMap(columnMap);
            if(users.isEmpty()){
                throw new AuthenticationException("User didn't existed!");
            }
            return users.get(0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    JwtToken

    package com.demo.token;
    
    import org.apache.shiro.authc.AuthenticationToken;
    
    public class JwtToken implements AuthenticationToken {
    
        // 密钥
        private String token;
    
        public JwtToken(String token) {
            this.token = token;
        }
    
        @Override
        public Object getPrincipal() {
            return token;
        }
    
        @Override
        public Object getCredentials() {
            return token;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    MyRealm

    package com.demo.realm;
    
    import com.demo.model.User;
    import com.demo.service.UserService;
    import com.demo.token.JwtToken;
    import com.demo.util.JwtUtil;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    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 org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    
    
    @Component
    @Slf4j
    public class MyRealm extends AuthorizingRealm {
    
    
        @Autowired
        private UserService userService;
    
        /**
         * 大坑!,必须重写此方法,不然Shiro会报错
         */
        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof JwtToken;
        }
    
        /**
         * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
         *
         * @param principals
         * @return
         */
        @SneakyThrows
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String username = JwtUtil.getUsername(principals.toString());
            User user = userService.getUser(username);
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addRole(user.getRole());
            Set<String> permission = new HashSet<>(Arrays.asList(user.getPermission().split(",")));
            simpleAuthorizationInfo.addStringPermissions(permission);
            return simpleAuthorizationInfo;
        }
    
        /**
         * 默认使用此方法认证用户名正确与否验证,错误抛出异常即可。
         *
         * @param auth
         * @return
         * @throws AuthenticationException
         */
        @SneakyThrows
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
            String token = (String) auth.getCredentials();
            // 解密获得username,用于和数据库进行对比
            String username = JwtUtil.getUsername(token);
            if (username == null) {
                throw new AuthenticationException("token invalid");
            }
            User user = userService.getUser(username);
            if (!JwtUtil.verify(token, username, user.getPassword())) {
                throw new AuthenticationException("Username or password error");
            }
            return new SimpleAuthenticationInfo(token, token, JwtUtil.SECRET);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    JWTFilter

    package com.demo.filter;
    
    import com.demo.token.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;
    
    /**
     * 代码的执行流程 preHandle -> isAccessAllowed -> isLoginAttempt -> executeLogin
     */
    @Slf4j
    public class JWTFilter extends BasicHttpAuthenticationFilter {
    
        /**
         * 判断用户是否想要登入。
         * 检测header里面是否包含Authorization字段即可
         */
        @Override
        protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
            HttpServletRequest req = (HttpServletRequest) request;
            String authorization = req.getHeader("Authorization");
            return authorization != null;
        }
    
        /**
         *
         */
        @Override
        protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String authorization = httpServletRequest.getHeader("Authorization");
    
            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());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    ShiroConfig

    package com.demo.config;
    
    import com.demo.filter.JWTFilter;
    import com.demo.realm.MyRealm;
    import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
    import org.apache.shiro.mgt.DefaultSubjectDAO;
    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.Map;
    
    @Configuration
    public class ShiroConfig {
    
        @Bean("securityManager")
        public DefaultWebSecurityManager getManager(MyRealm realm) {
            DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
            // 使用自己的realm
            manager.setRealm(realm);
    
            /*
             * 关闭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);
            manager.setSubjectDAO(subjectDAO);
    
            return manager;
        }
    
        @Bean("shiroFilterFactoryBean")
        public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
            ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    
            // 添加自己的过滤器并且取名为jwt
            Map<String, Filter> filterMap = new HashMap<>();
            filterMap.put("jwt", new JWTFilter());
            factoryBean.setFilters(filterMap);
    
            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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    TestController

    URL作用
    /login登入
    /article所有人都可以访问,但是用户与游客看到的内容不同
    /require_auth登入的用户才可以进行访问
    /require_roleadmin的角色用户才可以登入
    /require_permission拥有view和edit权限的用户才可以访问

    测试代码如下:

    package com.demo.controller;
    
    import com.demo.common.Result;
    import com.demo.model.User;
    import com.demo.service.UserService;
    import com.demo.util.JwtUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.apache.shiro.authz.annotation.Logical;
    import org.apache.shiro.authz.annotation.RequiresAuthentication;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.*;
    
    import javax.security.sasl.AuthenticationException;
    
    @Slf4j
    @RestController
    @RequestMapping("/jwt")
    public class TestController {
    
        @Autowired
        private UserService userService;
    
        @PostMapping("/login")
        public Result login(@RequestParam("username") String username, @RequestParam("password") String password) throws AuthenticationException {
            User user = userService.getUser(username);
            if (user.getPassword().equals(password)) {
                return new Result(200, "Login success", JwtUtil.sign(username, password));
            } else {
                throw new UnauthorizedException();
            }
        }
    
        @GetMapping("/article")
        public Result article() {
            Subject subject = SecurityUtils.getSubject();
            if (subject.isAuthenticated()) {
                return new Result(200, "You are already logged in", null);
            } else {
                return new Result(200, "You are guest", null);
            }
        }
    
        @GetMapping("/require_auth")
        @RequiresAuthentication
        public Result requireAuth() {
            return new Result(200, "You are authenticated", null);
        }
    
        @GetMapping("/require_role")
        @RequiresRoles("admin")
        public Result requireRole() {
            return new Result(200, "You are visiting require_role", null);
        }
    
        @GetMapping("/require_permission")
        @RequiresPermissions(logical = Logical.AND, value = {"view", "edit"})
        public Result requirePermission() {
            return new Result(200, "You are visiting permission require edit,view", null);
        }
    
        @RequestMapping(path = "/401")
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public Result unauthorized() {
            return new Result(401, "Unauthorized", null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    测试

    登录成功
    登录成功
    权限验证成功

  • 相关阅读:
    idea免费插件分享
    一键自动化博客发布工具,用过的人都说好(oschina篇)
    系统架构与Tomcat的安装和配置
    资料分析细节公式弥补(关于城乡人均成比例)
    基于gis三维可视化的智慧城市行业运用
    分布式锁的3种实现!附代码
    milvus upsert流程源码分析
    【快手小玩法-弹幕游戏】开发者功能测试报告提交模板
    Redis(04)| 数据结构-压缩列表
    视频教程下载:用ChatGPT玩转海外抖音TikTok
  • 原文地址:https://blog.csdn.net/u011047968/article/details/127856200