<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>

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

          共 28873字,需瀏覽 58分鐘

           ·

          2021-04-23 15:37

          來源:juejin.cn/post/6844903887871148039

          1. 前言

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

          Shiro有三大核心組件:

          • Subject: 即當(dāng)前用戶,在權(quán)限管理的應(yīng)用程序里往往需要知道誰能夠操作什么,誰擁有操作該程序的權(quán)利,shiro中則需要通過Subject來提供基礎(chǔ)的當(dāng)前用戶信息,Subject 不僅僅代表某個(gè)用戶,與當(dāng)前應(yīng)用交互的任何東西都是Subject,如網(wǎng)絡(luò)爬蟲等。所有的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)限人證器,我們需要自己來實(shí)現(xiàn)Realm來自定義的管理我們自己系統(tǒng)內(nèi)部的權(quán)限規(guī)則。SecurityManager要驗(yàn)證用戶,需要從Realm中獲取用戶。可以把Realm看做是數(shù)據(jù)源。

          2. 數(shù)據(jù)庫設(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:
            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 實(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"沒有權(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,我們可以通過統(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

          /**
           * 負(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ī)則
               * @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");        // 進(jìn)行身份認(rèn)證后才能訪問
                  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 {
                      // 登錄驗(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)限

          6.2 user用戶和vip用戶測(cè)試略

          7. 總結(jié)

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

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

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

          1. 以為精通Java 線程池,看到這些誤區(qū),還是年輕了...

          2. Spring Boot 集成 WebSocket,輕松實(shí)現(xiàn)信息推送!

          3. try-catch-finally 和 return 是怎么執(zhí)行的?

          4. 在 SpringBoot 項(xiàng)目中,Spring Security 和 Shiro 該如何選擇?

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 30
          點(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>
                  免费看日韩一级片 | 中国黄色操逼大片 | 亚洲最大S8SP | 国产一区二三区免费A片惊变 | 黄色一极视频 |