springboot整合shiro实现认证和授权(非常详细)

news2024/11/26 7:25:38

Shiro和Spring Sercurity应该是我们比较常用的权限框架了,这篇文章教大家怎么通过springboot整合shiro从0开始搭建一个包含权限控制的后台管理系统。

第一步:创建一个springboot项目

创建springboot项目,这里项目就命名为shiro

第二步:pom.xml中添加maven的依赖

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.9</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
        <shiro.version>1.3.2</shiro.version>
        <mysql.version>8.0.28</mysql.version>
        <druid.version>1.1.21</druid.version>
        <lombok.version>1.18.22</lombok.version>
        <fastjson.version>2.0.8</fastjson.version>
        <mybatis-boot.version>2.2.2</mybatis-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-boot.version}</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

 第三步:修改系统的配置文件application.yml

spring:
  # 激活profiles配置
  profiles:
    active: dev
  servlet:
    multipart:
      # 文件上传大小设置
      max-file-size: 1024MB
      max-request-size: 1024MB

# mybatis的mapper.xml文件的位置
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml

application-dev.yml

spring:
  # 配置数据源
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/shiro
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  # 只返回不为null的数据
  jackson:
    default-property-inclusion: non_null

# 设置启动端口号
server:
  port: 8080

第四步:新建数据库shiro,创建权限管理的五张表

/*
 Navicat Premium Data Transfer

 Source Server         : MariaDB
 Source Server Type    : MariaDB
 Source Server Version : 100605 (10.6.5-MariaDB)
 Source Host           : localhost:3306
 Source Schema         : shiro

 Target Server Type    : MariaDB
 Target Server Version : 100605 (10.6.5-MariaDB)
 File Encoding         : 65001

 Date: 02/07/2023 20:58:37
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
  `type` tinyint(3) UNSIGNED NOT NULL COMMENT '权限类型(父权限/子权限)',
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '接口路径',
  `method` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 COMMENT '请求方式(0-get;1-post)',
  `service` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'mhxysy' COMMENT '服务名',
  `parent_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '父级权限id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统权限表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '名称',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '描述',
  `sort` int(10) UNSIGNED NULL DEFAULT 0 COMMENT '自定义排序序号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统角色表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, '超级管理员', '最高权限,拥有系统所有权限', 0);
INSERT INTO `role` VALUES (2, '系统管理员', '拥有系统设置相关权限', 100);

-- ----------------------------
-- Table structure for role_permission
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `role_id` int(10) UNSIGNED NOT NULL COMMENT '角色id',
  `permission_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色-权限关联表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_permission
-- ----------------------------

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '12345' COMMENT '密码',
  `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '手机号',
  `gender` tinyint(4) UNSIGNED NOT NULL COMMENT '性别,数据来源于性别代码表(gender)的主键',
  `is_enable` tinyint(4) UNSIGNED NOT NULL COMMENT '是否启用(0-未启用;1-启用中)',
  `last_login_time` datetime NULL DEFAULT NULL COMMENT '上一次登录时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('2023', '系统管理员', 'system', '', '18888888888', 2, 1, '2022-11-25 00:15:42');
INSERT INTO `user` VALUES ('mhxy1218', '沐雨橙风ιε', 'mumu', 'mhxy1218', '16666666666', 1, 1, '2023-07-02 00:00:29');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `role_id` int(10) UNSIGNED NOT NULL COMMENT '角色id,数据来源于role表的主键',
  `user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户id,数据来源于user表的主键',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户-角色关系表表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 'mhxy1218');
INSERT INTO `user_role` VALUES (2, 3, '2023');

SET FOREIGN_KEY_CHECKS = 1;

第五步:创建5张表对应的实体类、mapper、service和controller,以及mapper.xml

可以在启动类或者配置类上使用注解@MapperScan开启mapper包扫描

@MapperScan("com.example.shiro.mapper")

第六步:创建UserRealm

项目根目录下创建realm包,在realm包下创建UserRealm.java,并继承AuthorizingRealm

package com.example.shiro.realm;

import com.example.shiro.entity.User;
import com.example.shiro.exception.GlobalException;
import com.example.shiro.mapper.UserMapper;
import com.example.shiro.restful.ResponseCode;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author heyunlin
 * @version 1.0
 */
@Component
public class UserRealm extends AuthorizingRealm {
    private final UserMapper mapper;

    @Autowired
    public UserRealm(UserMapper mapper) {
        this.mapper = mapper;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        // 得到用户名
        String username = token.getUsername();
        // 根据用户名查询用户信息
        User user = mapper.selectByUsername(username);

        if (user == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "登录失败,用户不存在~");
        }
        if (user.getIsEnable()) {
            String password = new String(token.getPassword());

            if (user.getPassword().equals(password)) {
                return new SimpleAuthenticationInfo(user, password, username);
            }
        }

        return null;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

}

第七步:创建Shiro的配置类

项目根目录下创建config包,在config包下创建ShiroConfig.java

package com.example.shiro.config;

import com.example.shiro.realm.UserRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro配置类
 */
@Configuration
public class ShiroConfig {
    /**
     * 配置安全管理器
     * @param userRealm UserRealm
     * @return DefaultWebSecurityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        securityManager.setRealm(userRealm);

        return securityManager;
    }

    /**
     * 配置Shiro过滤器工厂
     * @param securityManager 安全管理器
     * @return ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 注册安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 当用户访问认证资源的时候,如果用户没有登录,那么就会跳转到该属性指定的页面
        shiroFilterFactoryBean.setLoginUrl("/login.html");

        // 定义资源访问规则
        Map<String, String> map = new LinkedHashMap<>();

        map.put("/", "authc");
        map.put("/html/*", "authc");
        map.put("/index.html", "authc");
        // 登录接口不需要鉴权
        map.put("/user/login", "anon");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        return shiroFilterFactoryBean;
    }

}

经过以上步骤之后,整个项目的包结构如下

第八步:实现用户登录功能

在UserController类中添加一个login()方法,使用UserLoginDTO对象接收前端传来的用户名和密码。

package com.example.shiro.controller;

import com.example.shiro.dto.UserLoginDTO;
import com.example.shiro.restful.JsonResult;
import com.example.shiro.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author heyunlin
 * @version 1.0
 */
@RestController
@RequestMapping(path = "/user", produces = "application/json;charset=utf-8")
public class UserController {
    private final UserService service;

    @Autowired
    public UserController(UserService service) {
        this.service = service;
    }

    @RequestMapping(path = "/login", method = RequestMethod.POST)
    public JsonResult<Void> login(UserLoginDTO loginDTO) {
        service.login(loginDTO);

        return JsonResult.success();
    }

}

UserServiceImpl中实现用户登录的业务代码,当我们调用Subject的login()方法时,会执行UserRealm下面的认证方法doGetAuthenticationInfo()

package com.example.shiro.service.impl;

import com.example.shiro.dto.UserLoginDTO;
import com.example.shiro.entity.User;
import com.example.shiro.exception.GlobalException;
import com.example.shiro.mapper.UserMapper;
import com.example.shiro.restful.ResponseCode;
import com.example.shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

/**
 * @author heyunlin
 * @version 1.0
 */
@Service
public class UserServiceImpl implements UserService {
    private final UserMapper mapper;

    @Autowired
    public UserServiceImpl(UserMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public void login(UserLoginDTO loginDTO) {
        String username = loginDTO.getUsername();

        // 查询用户信息
        User user = mapper.selectByUsername(username);

        if (user != null) {
            // 如果用户被锁定,提前退出
            if (user.getIsEnable()) {
                // shiro登录认证
                UsernamePasswordToken token = new UsernamePasswordToken(username, loginDTO.getPassword());
                Subject subject = SecurityUtils.getSubject();

                subject.login(token);
                // 设置session失效时间:永不超时
                subject.getSession().setTimeout(-1001);

                // 修改管理员上一次登录时间
                user.setLastLoginTime(LocalDateTime.now());
                mapper.updateById(user);
            } else {
                throw new GlobalException(ResponseCode.FORBIDDEN, "账号已被锁定,禁止登录!");
            }
        } else {
            throw new GlobalException(ResponseCode.NOT_FOUND, "用户名不存在~");
        }
    }

}

第九步:创建几个简单的页面试一下效果

准备工作:在resources目录下创建static目录,存放静态资源文件,在static目录下创建js和html目录,把jquery复制到js目录下。

static目录下创建一个登录页面login.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>登录页面</title>
    </head>

    <body>
        <form id="loginForm">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input id="username" /></td>
                </tr>

                <tr>
                    <td>密码</td>
                    <td><input type="password" id="password" /></td>
                </tr>

                <tr>
                    <td>
                        <button type="button" id="login">登录</button>
                    </td>
                    <td>
                        <button type="reset">重置</button>
                    </td>
                </tr>
            </table>
        </form>

        <script src="/js/jquery.min.js"></script>
        <script src="/js/login.js"></script>
    </body>
</html>

js目录下创建login.js,点击登录按钮时提交用户的数据到接口/user/login,完成登录操作

$(document).ready(function () {
    $("#login").click(function () {
        let username = $("#username").val();
        let password = $("#password").val();

        $.post("/user/login", {
            username: username,
            password: password
        }, function (res) {
           if (res.code === 200) {
               location.href = "/html/home.html";
           }
        });
    });

});

当我们访问localhost:8080/login.html时,输入mumu/mhxy1218,点击登录时,会跳转到/html/home.html。然后我们清空浏览器缓存,刷新以下页面,发现跳回了/login.html。这是因为在shiro配置类里配置了html目录下所有的资源都要身份认证之后才能访问。

map.put("/html/*", "authc");

第十步:实现授权

接下来,讲解如何通过shiro完成鉴权,在UserRealm里的doGetAuthorizationInfo()方法中实现鉴权的代码,查询用户的权限保存到shiro中,为了方便演示效果,我们模拟几条数据。

package com.example.shiro.realm;

import com.example.shiro.entity.User;
import com.example.shiro.exception.GlobalException;
import com.example.shiro.mapper.UserMapper;
import com.example.shiro.restful.ResponseCode;
import org.apache.shiro.authc.*;
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.HashSet;
import java.util.Set;

/**
 * @author heyunlin
 * @version 1.0
 */
@Component
public class UserRealm extends AuthorizingRealm {
    private final UserMapper mapper;

    @Autowired
    public UserRealm(UserMapper mapper) {
        this.mapper = mapper;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        // 得到用户名
        String username = token.getUsername();
        // 根据用户名查询用户信息
        User user = mapper.selectByUsername(username);

        if (user == null) {
            throw new GlobalException(ResponseCode.BAD_REQUEST, "登录失败,用户不存在~");
        }
        if (user.getIsEnable()) {
            String password = new String(token.getPassword());

            if (user.getPassword().equals(password)) {
                return new SimpleAuthenticationInfo(user, password, username);
            } else {
                throw new GlobalException(ResponseCode.BAD_REQUEST, "用户名或密码错误,登录失败!");
            }
        }

        return null;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        String username = user.getUsername();

        // todo 通过用户名获取用户的权限
        Set<String> permissions = new HashSet<>();

        permissions.add("/user/delete");
        permissions.add("/user/update");

        authorizationInfo.setStringPermissions(permissions);

        return authorizationInfo;
    }

}

在UserController中添加两个方法delete()和update()

package com.example.shiro.controller;

import com.example.shiro.dto.UserLoginDTO;
import com.example.shiro.restful.JsonResult;
import com.example.shiro.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author heyunlin
 * @version 1.0
 */
@RestController
@RequestMapping(path = "/user", produces = "application/json;charset=utf-8")
public class UserController {
    private final UserService service;

    @Autowired
    public UserController(UserService service) {
        this.service = service;
    }

    @RequestMapping(path = "/login", method = RequestMethod.POST)
    public JsonResult<Void> login(UserLoginDTO loginDTO) {
        service.login(loginDTO);

        return JsonResult.success();
    }

    @RequestMapping(path = "/delete", method = RequestMethod.GET)
    public JsonResult<Void> delete() {
        return JsonResult.success("删除成功");
    }

    @RequestMapping(path = "/update", method = RequestMethod.POST)
    public JsonResult<Void> update() {
        return JsonResult.success("修改成功");
    }
 
}

home.html新增两个按钮

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>系统首页</title>
    </head>

    <body>
        <h1>欢迎来到系统首页!</h1>
        <a href="/user/delete">删除</a> | <button type="button" id="update">修改</button>
    
        <script>
            $(function () {
                $("#update").click(function () {
                    $.post("/user/update", function (res) {
                        if (res.code === 200) {
                            alert(res.message);
                        }
                    });
                });
            });
        </script>
    </body>
</html>

自定义过滤器,实现鉴权功能

没有权限访问时抛出异常会有一个问题:过滤器中抛出的异常是不会被统一异常处理器处理的。把抛出异常的代码改成直接返回响应对象JsonResult

package com.example.shiro.filter;

import com.alibaba.fastjson.JSON;
import com.example.shiro.restful.JsonResult;
import com.example.shiro.restful.ResponseCode;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 鉴权过滤器
 * @author heyunlin
 * @version 1.0
 */
@WebFilter("authorizationFilter")
public class AuthorizationFilter implements Filter {
    /**
     * 静态资源文件/文件夹
     */
    private static final List<String> STATIC_RESOURCES;

    static {
        STATIC_RESOURCES = new ArrayList<>();

        STATIC_RESOURCES.add("/index.html");
        STATIC_RESOURCES.add("/login.html");
        STATIC_RESOURCES.add("/images/");
        STATIC_RESOURCES.add("/html/");
        STATIC_RESOURCES.add("/css/");
        STATIC_RESOURCES.add("/js/");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        String requestURI = request.getRequestURI();

        if ("/".equals(requestURI) || "/user/login".equals(requestURI)) {
            chain.doFilter(req, resp);
        }
        for (String resource : STATIC_RESOURCES) {
            if (requestURI.contains(resource)) {
                chain.doFilter(req, resp);
            }
        }

        Subject subject = SecurityUtils.getSubject();

        if (subject != null && !subject.isPermitted(requestURI)) {
            HttpServletResponse response = (HttpServletResponse) resp;
            response.setContentType("application/json;charset=utf-8");

            // 构建返回对象
            JsonResult<Void> jsonResult= JsonResult.error(ResponseCode.UNAUTHORIZED, "正在访问未授权的资源~");
            String data = JSON.toJSONString(jsonResult);

            response.getWriter().write(data);
            return;
        }

        chain.doFilter(req, resp);
    }

}

好了,文章就分享到这里了,创作不易,如果看完这篇文章感觉对你有所帮助,不要忘了点赞+收藏哦~

文章相关的项目代码已开源,可按需获取:

springboot整合shiro实现认证和授权https://gitee.com/he-yunlin/shiro.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/715935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何用一键抠图工具在线压缩PNG图片

我们会发现&#xff0c;现在用的很多图片格式都是PNG&#xff0c;它通常以无损压缩的方式存储图像。然而&#xff0c;有时候我们可能会遇到PNG文件过大的问题&#xff0c;这样会导致文件传输、存储和加载速度变慢。怎么才能解决这个问题&#xff0c;有什么好用的工具推荐么&…

Linux--多个源文件编译:gcc -o 目标文件 源文件1 源文件2 ...

分析编译过程&#xff1a; 我们编译&#xff0c;实际上是针对.c文件。这里是main.c和test.c。 编译器在预处理的时候会将.h拷贝进.c文件。这里是test.h拷贝到test.c中 .c文件编译后变成.o目标文件。这里main.c和test.c文件编译后变成main.o和test.o 生成的.o文件再和lib.so…

CAD版本转换器有哪些?这些工具轻松转换CAD版本

当我们共享CAD文件时&#xff0c;如果接收人使用的CAD软件版本与发送人不同&#xff0c;则需要将CAD文件转换为接收人能够打开的版本。例如&#xff0c;当您使用的CAD软件版本为AutoCAD 2019&#xff0c;而接收人使用的CAD软件版本为AutoCAD 2017时&#xff0c;您需要将CAD文件…

【PCBA方案设计】电子体温计技术解决方案

体温计&#xff0c;是一种常用于医疗的小型测量产品&#xff0c;体温计通常分为汞体温计和电子体温计&#xff0c;汞体温计的原理非常简单&#xff0c;当汞加热时&#xff0c;它会膨胀&#xff0c;沿着非常狭窄的玻璃管上升。因此&#xff0c;我们将玻璃按一定比例划分&#xf…

windows 如何ping ip 加端口号

先在系统中找到 勾选上 和平时的pingip命令不一样了 现在是telnet ip 端口号 先看一组无法ping通端口号的案例 若telnet 127.0.0.1 6379 跳转至一个空白界面 代表ping此ip的端口号成功

搭建高性能数据库集群之二:MySQL读写分离(基于mycat)

一、MyCat概述 Mycat是数据库中间件。 使用场景&#xff1a; 高可用性与Mysql读写分离 业务数据分级存储大表水平拆分&#xff0c;集群并行计算数据库连接池整合多个数据源整合 安装 下载地址&#xff1a;http://dl.mycat.org.cn 安装Mycat之前&#xff0c;需要安装Mysq…

拜托!电梯机房还不会用这个技巧,就真的晚啦!

电梯是现代社会中不可或缺的交通工具之一&#xff0c;为了确保乘客的安全和顺畅运行&#xff0c;电梯机房的监控是至关重要的环节之一。 电梯机房烟感监控是现代建筑安全管理中非常重要的一环。随着城市高楼大厦的不断增多&#xff0c;电梯作为垂直交通的主要方式&#xff0c;其…

一篇文章告诉你VR全景展示的优势

引言 随着科技的不断进步&#xff0c;VR全景展示作为一项创新的技术应用&#xff0c;正以其独特的优势引领着营销行业的发展潮流。通过虚拟现实技术&#xff0c;VR全景展示为消费者带来了身临其境的观展体验&#xff0c;打破了传统展览的时空限制。 一&#xff0e;沉浸式体验 …

CH32V3xx CAN接口基本使用

1、CH32V3xx CAN简介 CH32V307具有2路CAN控制器,CAN控制器提供了完整的CAN协议实现方案,兼容CAN 规范2.0 A和2.0 B。可编程的传输速率,最高可达1Mbit/s。支持时间触发功能,可避免低优先级消息阻塞。支持3个发送邮箱,发送报文优先级可由报文标识或发送请求次序决定。支持3级…

【kubernetes系列】Kubernetes之生命周期和策略

概述 Pod 遵循一个预定义的生命周期&#xff0c;起始于 Pending 阶段&#xff0c;如果至少 其中有一个主要容器正常启动&#xff0c;则进入 Running&#xff0c;之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。 在 Pod 运行期间&#xff0c;k…

搭建Docker私有仓库

-v 主机到容器的映射&#xff08;卷的挂载&#xff1a;主机和容器的共享&#xff09; 创建容器一定要看成没成功 curl命令 访问浏览器命令 仓库装软件&#xff0c;配置------------- localhost 本机 127.0.0.1 改配置&#xff0c;重启容器 背景介绍 Docker regist…

数据仓库性能测试方法论与工具集

目录 文章目录 目录数据仓库 v.s. 传统数据库数据仓库性能测试案例性能指标测试方案测试场景测试数据集测试用例性能指标测试脚本工具 基准环境准备硬件环境软件环境 测试操作步骤Cloudwave 执行步骤导入数据集TestCase 1. 执行 13 条标准 SQL 测试语句TestCase 2. 执行多表联合…

Java Web JavaScript (3)23.7.3

7&#xff0c;事件监听 要想知道什么是事件监听&#xff0c;首先先聊聊什么是事件&#xff1f; HTML 事件是发生在 HTML 元素上的“事情”。比如&#xff1a;页面上的 按钮被点击、鼠标移动到元素之上、按下键盘按键 等都是事件。 事件监听是JavaScript 可以在事件被侦测到时…

人群密度技术人流量检测

1.贡献 本文的task还是在synthetic的data上训练&#xff0c;在real data上测试&#xff0c;并且之前的方法主要是先将synthetic的data风格转化成real的风格&#xff0c;再训练&#xff0c;来减轻domain的gap带来的问题&#xff0c;但是之前的这样做存在几个问题&#xff0c;第…

基于HTML的图书管理系统(源码+数据库)

一、相关技术介绍 1.HTML 5技术 HTML5作为构造Web内容的语言描述方式和互联网的规范&#xff0c;为网站设计提供了一种全新的思路。将HTML5引入到网站开发领域中&#xff0c;以提高用户体验度&#xff0c;使网站更具有吸引力&#xff0c;增强搜索引擎收录以及增加浏览次数等功…

B063-基础环境-后端环境 Lombok Restful postman swagger

目录 项目介绍需求分析项目架构项目技术架构前后端分离前后端分离开发模式前后端分离优缺点 后端项目搭建-部门模块项目端口规划创建SpringBoot项目流程整合SSM引用属性传值 LombokRestful风格Postman接口测试swagger接口规范其他 项目介绍 本项目是一个&#xff08;Online To…

​2023年湖北黄冈安管三类人员个人可以报名吗 ?报名时间是什么时候?

2023年湖北黄冈安管三类人员个人可以报名吗 &#xff1f;报名时间是什么时候&#xff1f; 个人不可以报名。安管三类人员包括建筑施工企业主要负责人、建筑施工企业项目负责人以及建筑施工企业专职安全生产管理人员&#xff0c;他们是对本企业安全生产工作负责的三类人员&#…

【LInux】简单进度条程序的编写

在进行进度条的程序的编写之前&#xff0c;还有几个问题需要解决。 第一个问题&#xff0c;先来看两段代码&#xff1a; 对比上面两段代码只有一个\n的区别。 左边的代码很简单&#xff0c;就是先打印&#xff0c;然后停顿 3 秒&#xff0c;程序退出。 主要问题是&#xff0c;…

ARM通讯接口

ARM要和传感器等芯片进行通信,需要物理线路.用于传送数据的物理线路,称为总线.根据传送的数据速度 物理线路的多少,有很多不同的总线.最常见的是 串口总线 SPI总线 I2C总线 USB总线等. 我们简单的介绍一下串口和i2c接口. 串口,UART,有两根线,分别为TX RX.TX用于发送本地数据…

SAP从入门到放弃系列之生产订单报工

文章目录导航 一、概述二、订单确认的方式2.1、订单抬头维度确认2.2、工订单序维度确认 三、确认的类型四、工序确认的屏幕界面4.1 设置屏幕格式4.2 订单类型维护确认参数 五、确认产生的数据 一、概述 生产订单下达后&#xff0c;订单的确认是对车间生产进度的体现&#xff0…