1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringBoot 集成 Shiro 極簡教程(實戰(zhàn)版)

        共 28809字,需瀏覽 58分鐘

         ·

        2021-05-11 08:10

        點擊關注公眾號,Java干貨及時送達

        真香!24W字的Java面試手冊(點擊查看)

        來源:juejin.cn/post/6844903887871148039

        1. 前言

        Apache Shiro是一個功能強大且易于使用的Java安全框架,提供了認證,授權,加密,和會話管理。

        Shiro有三大核心組件:

        • Subject: 即當前用戶,在權限管理的應用程序里往往需要知道誰能夠操作什么,誰擁有操作該程序的權利,shiro中則需要通過Subject來提供基礎的當前用戶信息,Subject 不僅僅代表某個用戶,與當前應用交互的任何東西都是Subject,如網絡爬蟲等。所有的Subject都要綁定到SecurityManager上,與Subject的交互實際上是被轉換為與SecurityManager的交互。
        • SecurityManager: 即所有Subject的管理者,這是Shiro框架的核心組件,可以把他看做是一個Shiro框架的全局管理組件,用于調度各種Shiro框架的服務。作用類似于SpringMVC中的DispatcherServlet,用于攔截所有請求并進行處理。
        • Realm: Realm是用戶的信息認證器和用戶的權限人證器,我們需要自己來實現(xiàn)Realm來自定義的管理我們自己系統(tǒng)內部的權限規(guī)則。SecurityManager要驗證用戶,需要從Realm中獲取用戶??梢园裄ealm看做是數(shù)據(jù)源。

        2. 數(shù)據(jù)庫設計

        2.1 User(用戶)

        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;

        -- ----------------------------
        -- Table structure for user
        -- ----------------------------
        DROP TABLE IF EXISTS `user`;
        CREATE TABLE `user`  (
          `id` bigint(20NOT NULL AUTO_INCREMENT,
          `password` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
          `username` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
          `account` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
          PRIMARY KEY (`id`USING BTREE
        ENGINE = MyISAM AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

        -- ----------------------------
        -- Records of user
        -- ----------------------------
        INSERT INTO `user` VALUES (1'root''超級用戶''root');
        INSERT INTO `user` VALUES (2'user''普通用戶''user');
        INSERT INTO `user` VALUES (3'vip''VIP用戶''vip');

        SET FOREIGN_KEY_CHECKS = 1;

        2.2 Role(角色)

        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;

        -- ----------------------------
        -- Table structure for role
        -- ----------------------------
        DROP TABLE IF EXISTS `role`;
        CREATE TABLE `role`  (
          `id` int(11NOT NULL AUTO_INCREMENT,
          `role` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
          `desc` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
          PRIMARY KEY (`id`USING BTREE
        ENGINE = MyISAM AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

        -- ----------------------------
        -- Records of role
        -- ----------------------------
        INSERT INTO `role` VALUES (1'admin''超級管理員');
        INSERT INTO `role` VALUES (2'user''普通用戶');
        INSERT INTO `role` VALUES (3'vip_user''VIP用戶');

        SET FOREIGN_KEY_CHECKS = 1;

        2.3 Permission(權限)

        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;

        -- ----------------------------
        -- Table structure for permission
        -- ----------------------------
        DROP TABLE IF EXISTS `permission`;
        CREATE TABLE `permission`  (
          `id` int(11NOT NULL AUTO_INCREMENT,
          `permission` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '權限名稱',
          `desc` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '權限描述',
          PRIMARY KEY (`id`USING BTREE
        ENGINE = MyISAM AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

        -- ----------------------------
        -- Records of permission
        -- ----------------------------
        INSERT INTO `permission` VALUES (1'add''增加');
        INSERT INTO `permission` VALUES (2'update''更新');
        INSERT INTO `permission` VALUES (3'select''查看');
        INSERT INTO `permission` VALUES (4'delete''刪除');

        SET FOREIGN_KEY_CHECKS = 1;

        2.4 User_Role(用戶-角色)

        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;

        -- ----------------------------
        -- Table structure for user_role
        -- ----------------------------
        DROP TABLE IF EXISTS `user_role`;
        CREATE TABLE `user_role`  (
          `id` int(11NOT NULL AUTO_INCREMENT,
          `user_id` int(11NULL DEFAULT NULL,
          `role_id` int(11NULL DEFAULT NULL,
          PRIMARY KEY (`id`USING BTREE
        ENGINE = MyISAM AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Fixed;

        -- ----------------------------
        -- Records of user_role
        -- ----------------------------
        INSERT INTO `user_role` VALUES (111);
        INSERT INTO `user_role` VALUES (222);
        INSERT INTO `user_role` VALUES (333);

        SET FOREIGN_KEY_CHECKS = 1;

        2.5 Role_Permission(角色-權限)

        SET NAMES utf8mb4;
        SET FOREIGN_KEY_CHECKS = 0;

        -- ----------------------------
        -- Table structure for role_permission
        -- ----------------------------
        DROP TABLE IF EXISTS `role_permission`;
        CREATE TABLE `role_permission`  (
          `id` int(11NOT NULL AUTO_INCREMENT,
          `role_id` int(11NULL DEFAULT NULL,
          `permission_id` int(255NULL DEFAULT NULL,
          PRIMARY KEY (`id`USING BTREE
        ENGINE = MyISAM AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Fixed;

        -- ----------------------------
        -- Records of role_permission
        -- ----------------------------
        INSERT INTO `role_permission` VALUES (111);
        INSERT INTO `role_permission` VALUES (212);
        INSERT INTO `role_permission` VALUES (313);
        INSERT INTO `role_permission` VALUES (414);
        INSERT INTO `role_permission` VALUES (523);
        INSERT INTO `role_permission` VALUES (633);
        INSERT INTO `role_permission` VALUES (732);
        INSERT INTO `role_permission` VALUES (821);

        SET FOREIGN_KEY_CHECKS = 1;

        3. 項目結構

        4. 前期準備

        4.1 導入Pom

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

        <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.2</version>
        </dependency>

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

        4.2 application.yml

        server:
          port: 8903
        spring:
          application:
            name: lab-user
          datasource:
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3306/laboratory?charset=utf8
            username: root
            password: root
        mybatis:
          type-aliases-package: cn.ntshare.laboratory.entity
          mapper-locations: classpath:mapper/*.xml
          configuration:
            map-underscore-to-camel-case: true

        4.3 實體類

        4.3.1 User.java

        @Data
        @ToString
        public class User implements Serializable {

            private static final long serialVersionUID = -6056125703075132981L;

            private Integer id;

            private String account;

            private String password;

            private String username;
        }

        4.3.2 Role.java

        @Data
        @ToString
        public class Role implements Serializable {

            private static final long serialVersionUID = -1767327914553823741L;

            private Integer id;

            private String role;

            private String desc;
        }

        4.4 Dao層

        4.4.1 PermissionMapper.java

        @Mapper
        @Repository
        public interface PermissionMapper {

            List<String> findByRoleId(@Param("roleIds") List<Integer> roleIds);
        }

        4.4.2 PermissionMapper.xml

        <mapper namespace="cn.ntshare.laboratory.dao.PermissionMapper">
            <sql id="base_column_list">
                id, permission, desc
            </sql>

            <select id="findByRoleId" parameterType="List" resultType="String">
                select permission
                from permission, role_permission rp
                where rp.permission_id = permission.id and rp.role_id in
                <foreach collection="roleIds" item="id" open="(" close=")" separator=",">
                    #{id}
                </foreach>
            </select>
        </mapper>

        4.4.3 RoleMapper.java

        @Mapper
        @Repository
        public interface RoleMapper {

            List<Role> findRoleByUserId(@Param("userId") Integer userId);
        }

        4.4.4 RoleMapper.xml

        <mapper namespace="cn.ntshare.laboratory.dao.RoleMapper">
            <sql id="base_column_list">
                id, user_id, role_id
            </sql>

            <select id="findRoleByUserId" parameterType="Integer" resultType="Role">
                select role.id, role
                from role, user, user_role ur
                where role.id = ur.role_id and ur.user_id = user.id and user.id = #{userId}
            </select>
        </mapper>

        4.4.5 UserMapper.java

        @Mapper
        @Repository
        public interface UserMapper {
            User findByAccount(@Param("account") String account);
        }

        4.4.6 UserMapper.xml

        <mapper namespace="cn.ntshare.laboratory.dao.UserMapper">

            <sql id="base_column_list">
                id, account, password, username
            </sql>

            <select id="findByAccount" parameterType="Map" resultType="User">
                select
                <include refid="base_column_list"/>
                from user
                where account = #{account}
            </select>
        </mapper>

        4.5 Service層

        4.5.1 PermissionServiceImpl.java

        @Service
        public class PermissionServiceImpl implements PermissionService {

            @Autowired
            private PermissionMapper permissionMapper;

            @Override
            public List<String> findByRoleId(List<Integer> roleIds) {
                return permissionMapper.findByRoleId(roleIds);
            }
        }

        4.5.2 RoleServiceImpl.java

        @Service
        public class RoleServiceImpl implements RoleService {

            @Autowired
            private RoleMapper roleMapper;

            @Override
            public List<Role> findRoleByUserId(Integer id) {
                return roleMapper.findRoleByUserId(id);
            }
        }

        4.5.3 UserServiceImpl.java

        @Service
        public class UserServiceImpl implements UserService {

            @Autowired
            private UserMapper userMapper;

            @Override
            public User findByAccount(String account) {
                return userMapper.findByAccount(account);
            }
        }

        4.6. 系統(tǒng)返回狀態(tài)枚舉與包裝函數(shù)

        4.6.1 ServerResponseEnum.java

        @AllArgsConstructor
        @Getter
        public enum ServerResponseEnum {
            SUCCESS(0"成功"),
            ERROR(10"失敗"),

            ACCOUNT_NOT_EXIST(11"賬號不存在"),
            DUPLICATE_ACCOUNT(12"賬號重復"),
            ACCOUNT_IS_DISABLED(13"賬號被禁用"),
            INCORRECT_CREDENTIALS(14"賬號或密碼錯誤"),
            NOT_LOGIN_IN(15"賬號未登錄"),
            UNAUTHORIZED(16"沒有權限")
            ;
            Integer code;
            String message;
        }

        4.6.2 ServerResponseVO.java

        @Getter
        @Setter
        @NoArgsConstructor
        public class ServerResponseVO<Timplements Serializable {
            private static final long serialVersionUID = -1005863670741860901L;
            // 響應碼
            private Integer code;

            // 描述信息
            private String message;

            // 響應內容
            private T data;

            private ServerResponseVO(ServerResponseEnum responseCode) {
                this.code = responseCode.getCode();
                this.message = responseCode.getMessage();
            }

            private ServerResponseVO(ServerResponseEnum responseCode, T data) {
                this.code = responseCode.getCode();
                this.message = responseCode.getMessage();
                this.data = data;
            }

            private ServerResponseVO(Integer code, String message) {
                this.code = code;
                this.message = message;
            }

            /**
             * 返回成功信息
             * @param data      信息內容
             * @param <T>
             * @return
             */

            public static<T> ServerResponseVO success(T data) {
                return new ServerResponseVO<>(ServerResponseEnum.SUCCESS, data);
            }

            /**
             * 返回成功信息
             * @return
             */

            public static ServerResponseVO success() {
                return new ServerResponseVO(ServerResponseEnum.SUCCESS);
            }

            /**
             * 返回錯誤信息
             * @param responseCode      響應碼
             * @return
             */

            public static ServerResponseVO error(ServerResponseEnum responseCode) {
                return new ServerResponseVO(responseCode);
            }
        }

        4.7 統(tǒng)一異常處理

        當用戶身份認證失敗時,會拋出UnauthorizedException,我們可以通過統(tǒng)一異常處理來處理該異常

        @RestControllerAdvice
        public class UserExceptionHandler {

            @ExceptionHandler(UnauthorizedException.class)
            @ResponseStatus(HttpStatus.UNAUTHORIZED)
            public ServerResponseVO UnAuthorizedExceptionHandler(UnauthorizedException e
        {
                return ServerResponseVO.error(ServerResponseEnum.UNAUTHORIZED);
            }
        }

        5. 集成Shiro

        5.1 UserRealm.java

        /**
         * 負責認證用戶身份和對用戶進行授權
         */

        public class UserRealm extends AuthorizingRealm {

            @Autowired
            private UserService userService;

            @Autowired
            private RoleService roleService;

            @Autowired
            private PermissionService permissionService;

            // 用戶授權
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                User user = (User) principalCollection.getPrimaryPrincipal();
                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                List<Role> roleList = roleService.findRoleByUserId(user.getId());
                Set<String> roleSet = new HashSet<>();
                List<Integer> roleIds = new ArrayList<>();
                for (Role role : roleList) {
                    roleSet.add(role.getRole());
                    roleIds.add(role.getId());
                }
                // 放入角色信息
                authorizationInfo.setRoles(roleSet);
                // 放入權限信息
                List<String> permissionList = permissionService.findByRoleId(roleIds);
                authorizationInfo.setStringPermissions(new HashSet<>(permissionList));

                return authorizationInfo;
            }

            // 用戶認證
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
                UsernamePasswordToken token = (UsernamePasswordToken) authToken;
                User user = userService.findByAccount(token.getUsername());
                if (user == null) {
                    return null;
                }
                return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
            }
        }

        5.2 ShiroConfig.java

        @Configuration
        public class ShiroConfig {

            @Bean
            public UserRealm userRealm() {
                return new UserRealm();
            }

            @Bean
            public DefaultWebSecurityManager securityManager() {
                DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
                securityManager.setRealm(userRealm());
                return securityManager;
            }

            /**
             * 路徑過濾規(guī)則
             * @return
             */

            @Bean
            public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
                ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
                shiroFilterFactoryBean.setSecurityManager(securityManager);

                shiroFilterFactoryBean.setLoginUrl("/login");
                shiroFilterFactoryBean.setSuccessUrl("/");
                Map<String, String> map = new LinkedHashMap<>();
                // 有先后順序
                map.put("/login""anon");      // 允許匿名訪問
                map.put("/**""authc");        // 進行身份認證后才能訪問
                shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
                return shiroFilterFactoryBean;
            }

            /**
             * 開啟Shiro注解模式,可以在Controller中的方法上添加注解
             * @param securityManager
             * @return
             */

            @Bean
            public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultSecurityManager securityManager) {
                AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
                authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
                return authorizationAttributeSourceAdvisor;
            }

        5.3 LoginController.java

        @RestController
        @RequestMapping("")
        public class LoginController {

            @PostMapping("/login")
            public ServerResponseVO login(@RequestParam(value = "account") String account,
                                          @RequestParam(value = "password") String password) 
        {
                Subject userSubject = SecurityUtils.getSubject();
                UsernamePasswordToken token = new UsernamePasswordToken(account, password);
                try {
                    // 登錄驗證
                    userSubject.login(token);
                    return ServerResponseVO.success();
                } catch (UnknownAccountException e) {
                    return ServerResponseVO.error(ServerResponseEnum.ACCOUNT_NOT_EXIST);
                } catch (DisabledAccountException e) {
                    return ServerResponseVO.error(ServerResponseEnum.ACCOUNT_IS_DISABLED);
                } catch (IncorrectCredentialsException e) {
                    return ServerResponseVO.error(ServerResponseEnum.INCORRECT_CREDENTIALS);
                } catch (Throwable e) {
                    e.printStackTrace();
                    return ServerResponseVO.error(ServerResponseEnum.ERROR);
                }
            }

            @GetMapping("/login")
            public ServerResponseVO login() {
                return ServerResponseVO.error(ServerResponseEnum.NOT_LOGIN_IN);
            }

            @GetMapping("/auth")
            public String auth() {
                return "已成功登錄";
            }

            @GetMapping("/role")
            @RequiresRoles("vip")
            public String role() {
                return "測試Vip角色";
            }

            @GetMapping("/permission")
            @RequiresPermissions(value = {"add""update"}, logical = Logical.AND)
            public String permission() {
                return "測試Add和Update權限";
            }
        }

        6. 測試

        6.1 用root用戶登錄

        6.1.1 登錄

        6.1.2 驗證是否登錄

        6.1.3 測試角色權限

        6.1.4 測試用戶操作權限

        6.2 user用戶和vip用戶測試略

        7. 總結

        本文演示了SpringBoot極簡集成Shiro框架,實現(xiàn)了基礎的身份認證和授權功能,如有不足,請多指教。

        后續(xù)可擴展的功能點有:

        • 集成Redis實現(xiàn)Shiro的分布式會話
        • 集成JWT實現(xiàn)單點登錄功能

        如有文章對你有幫助,

        在看”和轉發(fā)是對我最大的支持!



        推薦, Java面試手冊 
        內容包括網絡協(xié)議、Java基礎、進階、字符串、集合、并發(fā)、JVM、數(shù)據(jù)結構、算法、MySQL、Redis、Mongo、Spring、SpringBoot、MyBatis、SpringCloud、Linux以及各種中間件(Dubbo、Nginx、Zookeeper、MQ、Kafka、ElasticSearch)等等...

        點擊文末“閱讀原文”可直達

        瀏覽 38
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            91午夜在线观看 | 再深点灬舒服灬免费A片日本 | 一级尻逼视频 | 男同桌上课硬了叫我帮忙揉弄 | 婷婷五月天乱伦小说 | 99精品国产一区二区三区四区阿崩 | 久久99久久精品国产香蕉直播 | 91久久国产露脸精品国产吴梦梦 | 97国产视频 | 女人被两根一起进3p一前一后 |