<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 極簡(jiǎn)集成 Shiro

          共 27035字,需瀏覽 55分鐘

           ·

          2021-05-28 03:44

          1. 前言

          Apache Shiro是一個(gè)功能強(qiáng)大且易于使用的Java安全框架,提供了認(rèn)證,授權(quán),加密,和會(huì)話管理。

          Shiro有三大核心組件:

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

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

          Realm:Realm是用戶的信息認(rèn)證器和用戶的權(quán)限人證器,我們需要自己來(lái)實(shí)現(xiàn)Realm來(lái)自定義的管理我們自己系統(tǒng)內(nèi)部的權(quán)限規(guī)則。SecurityManager要驗(yàn)證用戶,需要從Realm中獲取用戶。可以把Realm看做是數(shù)據(jù)源。

          2. 數(shù)據(jù)庫(kù)設(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''超級(jí)用戶''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''超級(jí)管理員');
          INSERT INTO `role` VALUES (2'user''普通用戶');
          INSERT INTO `role` VALUES (3'vip_user''VIP用戶');

          SET FOREIGN_KEY_CHECKS = 1;


          2.3 Permission(權(quán)限)

          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 '權(quán)限名稱',
            `desc` varchar(255CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '權(quán)限描述',
            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(角色-權(quán)限)

          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. 項(xiàng)目結(jié)構(gòu)

          4. 前期準(zhǔn)備


          4.1 導(dǎo)入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 實(shí)體類

          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"賬號(hào)不存在"),
              DUPLICATE_ACCOUNT(12"賬號(hào)重復(fù)"),
              ACCOUNT_IS_DISABLED(13"賬號(hào)被禁用"),
              INCORRECT_CREDENTIALS(14"賬號(hào)或密碼錯(cuò)誤"),
              NOT_LOGIN_IN(15"賬號(hào)未登錄"),
              UNAUTHORIZED(16"沒(méi)有權(quán)限")
              ;
              Integer code;
              String message;
          }


          4.6.2 ServerResponseVO.java


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

              // 描述信息
              private String message;

              // 響應(yīng)內(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);
              }

              /**
               * 返回錯(cuò)誤信息
               * @param responseCode 響應(yīng)碼
               * @return
               */

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


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

          當(dāng)用戶身份認(rèn)證失敗時(shí),會(huì)拋出UnauthorizedException,我們可以通過(guò)統(tǒng)一異常處理來(lái)處理該異常

          @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


          /**
           * 負(fù)責(zé)認(rèn)證用戶身份和對(duì)用戶進(jìn)行授權(quán)
           */

          public class UserRealm extends AuthorizingRealm {

              @Autowired
              private UserService userService;

              @Autowired
              private RoleService roleService;

              @Autowired
              private PermissionService permissionService;

              // 用戶授權(quán)
              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);
                  // 放入權(quán)限信息
                  List<String> permissionList = permissionService.findByRoleId(roleIds);
                  authorizationInfo.setStringPermissions(new HashSet<>(permissionList));

                  return authorizationInfo;
              }

              // 用戶認(rèn)證
              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ò)濾規(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"); // 允許匿名訪問(wèn)
                  map.put("/**""authc"); // 進(jìn)行身份認(rèn)證后才能訪問(wèn)
                  shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
                  return shiroFilterFactoryBean;
              }

              /**
               * 開(kāi)啟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 {
                      // 登錄驗(yàn)證
                      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 "測(cè)試Vip角色";
              }

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


          6. 測(cè)試


          6.1 用root用戶登錄


          6.1.1 登錄
          6.1.2 驗(yàn)證是否登錄
          6.1.3 測(cè)試角色權(quán)限


          6.1.4 測(cè)試用戶操作權(quán)限

          7. 總結(jié)

          本文演示了 Spring Boot 極簡(jiǎn)集成 Shiro 框架,實(shí)現(xiàn)了基礎(chǔ)的身份認(rèn)證和授權(quán)功能,如有不足,請(qǐng)多指教。

          后續(xù)可擴(kuò)展的功能點(diǎn)有:

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

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

          鏈接:juejin.cn/post/6844903887871148039

          推薦閱讀:

          世界的真實(shí)格局分析,地球人類社會(huì)底層運(yùn)行原理

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf

          數(shù)字化轉(zhuǎn)型的本質(zhì)(10個(gè)關(guān)鍵詞)

          超詳細(xì)280頁(yè)Docker實(shí)戰(zhàn)文檔!開(kāi)放下載

          華為大數(shù)據(jù)解決方案(PPT)

          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产人人操 | 东京热最新网址 | 亚洲国产天堂 | 91av影视 | 伊人大香蕉视频网站 |