Shiro整合springboot以及自定義Realm


判斷用戶是否是游客身份,如果是游客身份則顯示此標簽內容

一、Shiro認證流程

 

 

 

二、SpringBoot應用整合Shiro

  • JavaSE應用中使用

  • web應用中使用

    • SSM整合Shiro(配置多,用的少)

    • SpringBoot應用整合Shiro

2.1 創建SpringBoot應用

  • lombok

  • spring web

  • thymeleaf

2.2 整合Druid和MyBatis

  • 依賴

  • <!-- druid starter -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
     <!--mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>

     

  • 配置
  • spring:
      datasource:
        druid:
          url: jdbc:mysql://47.96.11.185:3306/test
          # MySQL如果是8.x   com.mysql.cj.jdbc.Driver
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password: admin123
          initial-size: 1
          min-idle: 1
          max-active: 20
    mybatis:
      mapper-locations: classpath:mappers/*Mapper.xml
      type-aliases-package: com.qfedu.springbootssm.beans

     

  • 2.3 整合Shiro

    • 導入依賴

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

      Shiro配置(java配置方式)

        • SpringBoot默認沒有提供對Shiro的自動配置

        • @Configuration
          public class ShiroConfig {
          
              @Bean
              public JdbcRealm getJdbcRealm(DataSource dataSource){
                  JdbcRealm jdbcRealm = new JdbcRealm();
                  //JdbcRealm會自行從數據庫查詢用戶及權限數據(數據庫的表結構要符合JdbcRealm的規范)
                  jdbcRealm.setDataSource(dataSource);
                  //JdbcRealm默認開啟認證功能,需要手動開啟授權功能
                  jdbcRealm.setPermissionsLookupEnabled(true);
                  return  jdbcRealm;
              }
          
              @Bean
              public DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm){
                  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
                  securityManager.setRealm(jdbcRealm);
                  return securityManager;
              }
          
              @Bean
              public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
                  ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
                  //過濾器就是shiro就行權限校驗的核心,進行認證和授權是需要SecurityManager的
                  filter.setSecurityManager(securityManager);
          
                  Map<String,String> filterMap = new HashMap<>();
                  filterMap.put("/","anon");
                  filterMap.put("/login.html","anon");
                  filterMap.put("/regist.html","anon");
                  filterMap.put("/user/login","anon");
                  filterMap.put("/user/regist","anon");
                  filterMap.put("/static/**","anon");
                  filterMap.put("/**","authc");
          
                  filter.setFilterChainDefinitionMap(filterMap);
                  filter.setLoginUrl("/login.html");
                  //設置未授權訪問的頁面路徑
                  filter.setUnauthorizedUrl("/login.html");
                  return filter;
              }
          
          }
          • 認證測試----------UserServiceImpl.java

            @Service
            public class UserServiceImpl {
            
                public void checkLogin(String userName,String userPwd) throws Exception{
                    Subject subject = SecurityUtils.getSubject();
                    UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
                    subject.login(token);
                }
            
            }

            UserController.java

          • @Controller
            @RequestMapping("user")
            public class UserController {
            
                @Resource
                private UserServiceImpl userService;
            
                @RequestMapping("login")
                public String login(String userName,String userPwd){
                    try {
                        userService.checkLogin(userName,userPwd);
                        System.out.println("------登錄成功!");
                        return "index";
                    } catch (Exception e) {
                        System.out.println("------登錄失敗!");
                        return "login";
                    }
            
                }
            }

        • 自定義Realm     
          • 自定義Realm       
            • 自定義Real
        /**
         * 1.創建一個類繼承AuthorizingRealm類(實現了Realm接口的類)
         * 2.重寫doGetAuthorizationInfo和doGetAuthenticationInfo方法
         * 3.重寫getName方法返回當前realm的一個自定義名稱
         */
        public class MyRealm extends AuthorizingRealm {
            
            @Resource
            private UserDAO userDAO;
            @Resource
            private RoleDAO roleDAO;
            @Resource
            private PermissionDAO permissionDAO;
        
            public String getName() {
                return "myRealm";
            }
            
            /**
             * 獲取授權數據(將當前用戶的角色及權限信息查詢出來)
             */
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                //獲取用戶的用戶名
                String username = (String) principalCollection.iterator().next();
                //根據用戶名查詢當前用戶的角色列表
                Set<String> roleNames = roleDAO.queryRoleNamesByUsername(username);
                //根據用戶名查詢當前用戶的權限列表
                Set<String> ps = permissionDAO.queryPermissionsByUsername(username);
        
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                info.setRoles(roleNames);
                info.setStringPermissions(ps);
                return info;
            }
        
            /**
             * 獲取認證的安全數據(從數據庫查詢的用戶的正確數據)
             */
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
                //參數authenticationToken就是傳遞的  subject.login(token)
                // 從token中獲取用戶名
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                String username = token.getUsername();
                //根據用戶名,從數據庫查詢當前用戶的安全數據
                User user = userDAO.queryUserByUsername(username);
        
                AuthenticationInfo info = new SimpleAuthenticationInfo(
                        username,           //當前用戶用戶名
                        user.getUserPwd(),   //從數據庫查詢出來的安全密碼
                        getName());
        
                return info;
            }
        }
      • 二、加密

        • 明文-----(加密規則)-----密文

        • 加密規則可以自定義,在項目開發中我們通常使用BASE64和MD5編碼方式

          • BASE64:可反編碼的編碼方式(對稱)

            • 明文----密文

            • 密文----明文

          • MD5: 不可逆的編碼方式(非對稱)

            • 明文----密文

        • 如果數據庫用戶的密碼存儲的密文,Shiro該如何完成驗證呢?

        • 使用Shiro提供的加密功能,對輸入的密碼進行加密之后再進行認證。

        2.1 加密介紹

    • 2.2 Shiro使用加密認證

      • 配置matcher

      • @Configuration
        public class ShiroConfig {
        
            //...
            @Bean
            public HashedCredentialsMatcher getHashedCredentialsMatcher(){
                HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
                //matcher就是用來指定加密規則
                //加密方式
                matcher.setHashAlgorithmName("md5");
                //hash次數
                matcher.setHashIterations(1);    //此處的循環次數要與用戶注冊是密碼加密次數一致
                return matcher;
            }
        
            //自定義Realm
            @Bean
            public MyRealm getMyRealm( HashedCredentialsMatcher matcher ){
                MyRealm myRealm = new MyRealm();
                myRealm.setCredentialsMatcher(matcher);
                return myRealm;
            }
        
            //...
        }

        2.3 用戶注冊密碼加密處理

        • registh.html

        • <form action="/user/regist" method="post">
              <p>帳號:<input type="text" name="userName"/></p>
              <p>密碼:<input type="text" name="userPwd"/></p>
              <p><input type="submit" value="提交注冊"/></p>
          </form>

          UserController

        • @Controller
          @RequestMapping("user")
          public class UserController {
          
              @Resource
              private UserServiceImpl userService;
          
          
          
              @RequestMapping("/regist")
              public String regist(String userName,String userPwd) {
                  System.out.println("------注冊");
          
                  //注冊的時候要對密碼進行加密存儲
                  Md5Hash md5Hash = new Md5Hash(userPwd);
                  System.out.println("--->>>"+ md5Hash.toHex());
          
                  //加鹽加密
                  int num = new Random().nextInt(90000)+10000;   //10000—99999
                  String salt = num+"";
                  Md5Hash md5Hash2 = new Md5Hash(userPwd,salt);
                  System.out.println("--->>>"+md5Hash2);
          
                  //加鹽加密+多次hash
                  Md5Hash md5Hash3 = new Md5Hash(userPwd,salt,3);
                  System.out.println("--->>>"+md5Hash3);
          
                  //SimpleHash hash = new SimpleHash("md5",userPwd,num,3);
                  
                  //將用戶信息保存到數據庫時,保存加密后的密碼,如果生成的隨機鹽,鹽也要保存
                  
                  return "login";
              }
          
          }

          2.4 如果密碼進行了加鹽處理,則Realm在返回認證數據時需要返回鹽

          • 在自定義Realm中:

      • public class MyRealm extends AuthorizingRealm {
            
            @Resource
            private UserDAO userDAO;
            @Resource
            private RoleDAO roleDAO;
            @Resource
            private PermissionDAO permissionDAO;
        
            public String getName() {
                return "myRealm";
            }
        
            /**
             * 獲取認證的安全數據(從數據庫查詢的用戶的正確數據)
             */
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
                //參數authenticationToken就是傳遞的  subject.login(token)
                // 從token中獲取用戶名
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                String username = token.getUsername();
                //根據用戶名,從數據庫查詢當前用戶的安全數據
                User user = userDAO.queryUserByUsername(username);
        
        //        AuthenticationInfo info = new SimpleAuthenticationInfo(
        //                username,           //當前用戶用戶名
        //                user.getUserPwd(),   //從數據庫查詢出來的安全密碼
        //                getName());
        
                //如果數據庫中用戶的密碼是加了鹽的
                AuthenticationInfo info = new SimpleAuthenticationInfo(
                        username,           //當前用戶用戶名
                        user.getUserPwd(),   //從數據庫查詢出來的安全密碼
                        ByteSource.Util.bytes(user.getPwdSalt()),
                        getName());
        
                return info;
            }
        }

        三、退出登錄

        • 在Shiro過濾器中進行配置,配置logut對應的路徑

        • filterMap.put("/exit","logout");

          在頁面的“退出”按鈕上,跳轉到logout對應的url

      • <a href="exit">退出</a>

        四、授權

        用戶登錄成功之后,要進行響應的操作就需要有對應的權限;在進行操作之前對權限進行檢查—授權

        權限控制通常有兩類做法:

        • 不同身份的用戶登錄,我們現在不同的操作菜單(沒有權限的菜單不現實)

        • 對所有用戶顯示所有菜單,當用戶點擊菜單以后再驗證當前用戶是否有此權限,如果沒有則提示權限不足

        4.1 HTML授權

        • 在菜單頁面只顯示當前用戶擁有權限操作的菜單

        • shiro標簽

      • <shiro:hasPermission name="sys:c:save">
            <dd><a href="javascript:;">入庫</a></dd>
        </shiro:hasPermission>

        4.2 過濾器授權

        • 在shiro過濾器中對請求的url進行權限設置

        • filterMap.put("/c_add.html","perms[sys:c:save]");
          
          //設置未授權訪問的頁面路徑—當權限不足時顯示此頁面
          filter.setUnauthorizedUrl("/lesspermission.html");
      • 4.3 注解授權

      • 配置Spring對Shiro注解的支持:ShiroConfig.java

      • @Bean
        public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            autoProxyCreator.setProxyTargetClass(true);
            return autoProxyCreator;
        }
        
        @Bean
        public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
      • 在請求的控制器添加權限注解
      • @Controller
        @RequestMapping("customer")
        public class CustomerController {
        
            @RequestMapping("list")
            //如果沒有 sys:k:find 權限,則不允許執行此方法
            @RequiresPermissions("sys:k:find")
            //    @RequiresRoles("")
            public String list(){
                System.out.println("----------->查詢客戶信息");
                return "customer_list";
            }
        
        }
      • 通過全局異常處理,指定權限不足時的頁面跳轉
      • @ControllerAdvice
        public class GlobalExceptionHandler {
        
            @ExceptionHandler
            public String doException(Exception e){
                if(e instanceof AuthorizationException){
                    return  "lesspermission";
                }
                return null;
            }
        
        }

        4.4 手動授權

      • 在代碼中進行手動的權限校驗

      • Subject subject = SecurityUtils.getSubject();
        if(subject.isPermitted("sys:k:find")){
            System.out.println("----------->查詢客戶信息");
            return "customer_list";
        }else{
            return "lesspermission";
        }
      • 五、緩存使用

      • 使用Shiro進行權限管理過程中,每次授權都會訪問realm中的doGetAuthorizationInfo方法查詢當前用戶的角色及權限信息,如果系統的用戶量比較大則會對數據庫造成比較大的壓力

        Shiro支持緩存以降低對數據庫的訪問壓力(緩存的是授權信息)

      • 5.1 導入依賴

      • <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>

        5.2 配置緩存策略

      • 在resources目錄下創建一個xml文件(ehcache.xml)

      • <?xml version="1.0" encoding="UTF-8"?>
        <ehcache updateCheck="false" dynamicConfig="false">
        
            <diskStore path="C:\TEMP" />
        
            <cache name="users"  timeToLiveSeconds="300"  maxEntriesLocalHeap="1000"/>
        
            <defaultCache name="defaultCache"
                          maxElementsInMemory="10000"
                          eternal="false"
                          timeToIdleSeconds="120"
                          timeToLiveSeconds="120"
                          overflowToDisk="false"
                          maxElementsOnDisk="100000"
                          diskPersistent="false"
                          diskExpiryThreadIntervalSeconds="120"
                          memoryStoreEvictionPolicy="LRU"/>
                    <!--緩存淘汰策略:當緩存空間比較緊張時,我們要存儲新的數據進來,就必然要刪除一些老的數據
                        LRU 最近最少使用
                        FIFO 先進先出
                        LFU  最少使用
                    -->
        </ehcache>

        5.3 加入緩存管理

      • ShiroConfig.java

      • @Bean
        public EhCacheManager getEhCacheManager(){
            EhCacheManager ehCacheManager = new EhCacheManager();
            ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
            return ehCacheManager;
        }
        
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(myRealm);
            securityManager.setCacheManager(getEhCacheManager());
            return securityManager;
        }
  • 六、session管理

    • Shiro進行認證和授權是基於session實現的,Shiro包含了對session的管理
    • 如果我們需要對session進行管理

    • 自定義session管理器

    • 將自定義的session管理器設置給SecurityManager

    • 配置自定義SessionManager:ShiroConfig.java

  • @Bean
    public DefaultWebSessionManager getDefaultWebSessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        System.out.println("----------"+sessionManager.getGlobalSessionTimeout()); // 1800000
        //配置sessionManager
        sessionManager.setGlobalSessionTimeout(5*60*1000);
        return sessionManager;
    }
    
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        securityManager.setCacheManager(getEhCacheManager());
        securityManager.setSessionManager(getDefaultWebSessionManager());
        return securityManager;
    }
  • 二、RememberMe

  •  

    將用戶對頁面訪問的權限分為三個級別:

    • 未認證—可訪問的頁面—(陌生人)—問候

    • login.html、regist.html

    • 記住我—可訪問的頁面—(前女友)—朋友間的擁抱

    • info.html

    • 已認證—可訪問的頁面—(現女友)—牽手

    • 轉賬.html

  • 2.1 在過濾器中設置“記住我”可訪問的url

  •  

    // anon     表示未認證可訪問的url
    // user     表示記住我可訪問的url(已認證也可以訪問)
    //authc     表示已認證可訪問的url
    //perms        表示必須具備指定的權限才可訪問
    //logout    表示指定退出的url
    filterMap.put("/","anon");
    filterMap.put("/index.html","user");
    filterMap.put("/login.html","anon");
    filterMap.put("/regist.html","anon");
    filterMap.put("/user/login","anon");
    filterMap.put("/user/regist","anon");
    filterMap.put("/layui/**","anon");
    filterMap.put("/**","authc");
    filterMap.put("/c_add.html","perms[sys:c:save]");
    filterMap.put("/exit","logout");

     

  • 2.2 在ShiroConfig.java中配置基於cookie的rememberMe管理器

  •  

    @Bean
    public CookieRememberMeManager cookieRememberMeManager(){
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
       
        //cookie必須設置name
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(30*24*60*60);
        
        rememberMeManager.setCookie(cookie);
        return  rememberMeManager;
    }
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        securityManager.setCacheManager(getEhCacheManager());
        securityManager.setSessionManager(getDefaultWebSessionManager());
        //設置remember管理器
        securityManager.setRememberMeManager(cookieRememberMeManager());
        return securityManager;
    }

     

  • 2.3 登錄認證時設置token“記住我”

    • 登錄頁面

    • <form action="/user/login" method="post">
          <p>帳號:<input type="text" name="userName"/></p>
          <p>密碼:<input type="text" name="userPwd"/></p>
          <p>記住我:<input type="checkbox" name="rememberMe"/></p>
          <p><input type="submit" value="登錄"/></p>
      </form>

       

    • 控制器
    • @Controller
      @RequestMapping("user")
      public class UserController {
      
          @Resource
          private UserServiceImpl userService;
      
          @RequestMapping("login")
          public String login(String userName,String userPwd,boolean rememberMe){
              try {
                  userService.checkLogin(userName,userPwd,rememberMe);
                  System.out.println("------登錄成功!");
                  return "index";
              } catch (Exception e) {
                  System.out.println("------登錄失敗!");
                  return "login";
              }
      
          }
          
          //...
      }

       

    • service
    • @Service
      public class UserServiceImpl {
      
          public void checkLogin(String userName, String userPwd,boolean rememberMe) throws Exception {
              //Shiro進行認證 ——入口
              Subject subject = SecurityUtils.getSubject();
              UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
              token.setRememberMe(rememberMe);
              subject.login(token);
          }
      }

       

    • 三、Shiro多Realm配置

      3.1 使用場景

      • 當shiro進行權限管理,數據來自於不同的數據源時,我們可以給SecurityManager配置多個Realm

        • 3.2 多個Realm的處理方式

          3.2.1 鏈式處理
          • 多個Realm依次進行認證

          3.2.2 分支處理
          • 根據不同的條件從多個Realm中選擇一個進行認證處理

          3.3 多Realm配置(鏈式處理)

          • 定義多個Realm

            • UserRealm

            • public class UserRealm extends AuthorizingRealm {
              
                  Logger logger = LoggerFactory.getLogger(UserRealm.class);
              
                  @Override
                  public String getName() {
                      return "UserRealm";
                  }
              
                  @Override
                  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                      return null;
                  }
              
                  @Override
                  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
                      logger.info("--------------------------------UserRealm");
                      //從token中獲取username
                      UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                      String username = token.getUsername();
                      //根據username從users表中查詢用戶信息
              
                      SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"123456",getName());
                      return info;
                  }
              }

               

            • ManagerRealm
            • public class ManagerRealm extends AuthorizingRealm {
              
                  Logger logger = LoggerFactory.getLogger(ManagerRealm.class);
              
                  @Override
                  public String getName() {
                      return "ManagerRealm";
                  }
              
                  @Override
                  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
                      return null;
                  }
              
                  @Override
                  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
                      logger.info("--------------------------------ManagerRealm");
                      //從token中獲取username
                      UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                      String username = token.getUsername();
                      //根據username從嗎managers表中查詢用戶信息
              
                      SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"222222",getName());
                      return info;
                  }
              }

               

            • 在ShiroConfig.java中為SecurityManager配置多個Realm
            • @Configuration
              public class ShiroConfig {
              
                  @Bean
                  public UserRealm userRealm(){
                      UserRealm userRealm = new UserRealm();
                      return  userRealm;
                  }
              
                  @Bean
                  public ManagerRealm managerRealm(){
                      ManagerRealm managerRealm = new ManagerRealm();
                      return managerRealm;
                  }
              
                  @Bean
                  public DefaultWebSecurityManager getDefaultWebSecurityManager(){
                      DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
                      
                      //securityManager中配置多個realm
                      Collection<Realm> realms = new ArrayList<>();


              realms.add(userRealm()); realms.add(managerRealm());

              securityManager.setRealms(realms);
              return securityManager; } //... }

               

    • 3.5 多Realm配置(分支處理)

    • 根據不同的條件執行不同的Realm
    • 實現案例:用戶不同身份登錄執行不同的Realm

      • 自定義Realm(UserRealm\ManagerRealm)

        • 當在登錄頁面選擇“普通用戶”登錄,則執行UserRealm的認證

        • 當在登錄頁面選擇“管理員”登錄,則執行ManagerRealm的認證

      • Realm的聲明及配置

      • 自定義Token

      • public class MyToken extends UsernamePasswordToken {
        
            private String loginType;
        
            public MyToken(String userName,String userPwd, String loginType) {
                super(userName,userPwd);
                this.loginType = loginType;
            }
        
            public String getLoginType() {
                return loginType;
            }
        
            public void setLoginType(String loginType) {
                this.loginType = loginType;
            }
        }

         

      • 自定義認證器
      • public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
        
            Logger logger = LoggerFactory.getLogger(MyModularRealmAuthenticator.class);
        
            @Override
            protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
                logger.info("------------------------------MyModularRealmAuthenticator");
        
                this.assertRealmsConfigured();
                Collection<Realm> realms = this.getRealms();
        
                MyToken token = (MyToken) authenticationToken;
                String loginType = token.getLoginType(); // User
                logger.info("------------------------------loginType:"+loginType);
        
                Collection<Realm> typeRealms = new ArrayList<>();
                for(Realm realm:realms){
                    if(realm.getName().startsWith(loginType)){  //UserRealm
                        typeRealms.add(realm);
                    }
                }
        
               if(typeRealms.size()==1){
                   return this.doSingleRealmAuthentication((Realm)typeRealms.iterator().next(), authenticationToken);
               }else{
                   return this.doMultiRealmAuthentication(typeRealms, authenticationToken);
               }
        
            }
        
        }

         

      • 配置自定義認證器
      • @Configuration
        public class ShiroConfig {
        
            @Bean
            public UserRealm userRealm(){
                UserRealm userRealm = new UserRealm();
                return  userRealm;
            }
        
            @Bean
            public ManagerRealm managerRealm(){
                ManagerRealm managerRealm = new ManagerRealm();
                return managerRealm;
            }
        
            @Bean
            public MyModularRealmAuthenticator myModularRealmAuthenticator(){
                MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();
                return myModularRealmAuthenticator;
            }
        
            @Bean
            public DefaultWebSecurityManager getDefaultWebSecurityManager(){
                DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
                //配置自定義認證器(放在realms設置之前)
                securityManager.setAuthenticator(myModularRealmAuthenticator());
        
                //securityManager中配置多個realm
                Collection<Realm> realms = new ArrayList<>();
                realms.add(userRealm());
                realms.add(managerRealm());
        
                securityManager.setRealms(realms);
                return securityManager;
            }
        
            //...
        
        }

         

      • 測試:控制器接受數據進行認證

        • login.html

        • <form action="user/login" method="post">
              <p>帳號:<input type="text" name="userName"/></p>
              <p>密碼:<input type="text" name="userPwd"/></p>
              <p><input type="radio" name="loginType" value="User" checked/>普通用戶
              <input type="radio" name="loginType" value="Manager"/>管理員</p>
          
              <p><input type="submit" value="登錄"/></p>
          </form>

           

        • UserController.java
        • @Controller
          @RequestMapping("user")
          public class UserController {
              Logger logger = LoggerFactory.getLogger(UserController.class);
          
              @RequestMapping("login")
              public String login(String userName,String userPwd, String loginType){
                  logger.info("~~~~~~~~~~~~~UserController-login");
                  try{
                      //UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
                      MyToken token = new MyToken(userName,userPwd,loginType);
                      Subject subject = SecurityUtils.getSubject();
                      subject.login(token);
                      return "index";
                  }catch (Exception e){
                      return "login";
                  }
          
              }
          
          }

           


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM