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>

        Spring Boot 極簡集成 Shiro 實戰(zhàn)

        共 27154字,需瀏覽 55分鐘

         ·

        2021-05-05 06:58

        1. 前言

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

        Shiro有三大核心組件:

        Subject:即當前用戶,在權限管理的應用程序里往往需要知道誰能夠操作什么,誰擁有操作該程序的權利,shiro中則需要通過Subject來提供基礎的當前用戶信息,Subject 不僅僅代表某個用戶,與當前應用交互的任何東西都是Subject,如網(wǎng)絡爬蟲等。所有的Subject都要綁定到SecurityManager上,與Subject的交互實際上是被轉(zhuǎn)換為與SecurityManager的交互。

        SecurityManager:即所有Subject的管理者,這是Shiro框架的核心組件,可以把他看做是一個Shiro框架的全局管理組件,用于調(diào)度各種Shiro框架的服務。作用類似于SpringMVC中的DispatcherServlet,用于攔截所有請求并進行處理。

        Realm:Realm是用戶的信息認證器和用戶的權限人證器,我們需要自己來實現(xiàn)Realm來自定義的管理我們自己系統(tǒng)內(nèi)部的權限規(guī)則。SecurityManager要驗證用戶,需要從Realm中獲取用戶。可以把Realm看做是數(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. 項目結(jié)構

        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:
          port8903
        spring:
          application:
            name: lab-user
          datasource:
            driver-class-name: com.mysql.jdbc.Driver
            urljdbc:mysql://127.0.0.1:3306/laboratory?charset=utf8
            username: root
            password: root
        mybatis:
          type-aliases-package: cn.ntshare.laboratory.entity
          mapper-locationsclasspath: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;

            // 響應內(nèi)容
            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 信息內(nèi)容
             * @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 測試用戶操作權限

        7. 總結(jié)

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

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

        1. 集成 Redis 實現(xiàn) Shiro 的分布式會話

        2. 集成 JWT 實現(xiàn)單點登錄功能

        鏈接:juejin.cn/post/6844903887871148039

        —————END—————

        推薦閱讀:

        谷歌發(fā)布新編程語言,專治SQL各種不服
        Java實現(xiàn)超市管理系統(tǒng),包含源代碼!
        Spring Boot 接入支付寶,實戰(zhàn)來了!
        Nginx 除了負載均衡,還能做什么?
        SpringBoot+MyBatis+docker電商項目,附帶超詳細的文檔!

        最近面試BAT,整理一份面試資料Java面試BAT通關手冊,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構等等。
        獲取方式:關注公眾號并回復 java 領取,更多內(nèi)容陸續(xù)奉上。
        明天見(??ω??)??
        瀏覽 29
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        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>
            啊~嗯短裙直接进去h师生视频 | 韩国三级电影网址 | 国产精品久久亚洲7777 | 丰满老熟好大bbb | 在线亚洲+欧美+日本专区 | 成人视频在线免费播放 | 欧美精品一区二区三区很污很色的 | 欧美三级片视频 | 国产精品激情偷乱一区二区∴ | 免费看男人添女人无遮挡 |