關(guān)于Shiro框架的學(xué)習(xí)(一)

前言

由于最近在做一個(gè)項(xiàng)目弟劲,剛完成到登錄注冊,不想和以前的項(xiàng)目搬同樣的磚了镶摘,想完成點(diǎn)不那么low的功能嗽桩,像單點(diǎn)登錄、權(quán)限控制等凄敢,于是就想起了Shiro框架碌冶。

初識(shí)Shiro

任何一種技術(shù)總有個(gè)開始,又總是這么巧涝缝,每個(gè)開始總是個(gè)HelloWorld扑庞。
官方給出的依賴:

shiro依賴

示例代碼:

public class FirstShiro {

    private static final transient Logger log = LoggerFactory.getLogger(FirstShiro.class);
    public static void main(String[] args) {
        // TODO Auto-generated method stub

            log.info("My First Apache Shiro Application");
            System.exit(0);
    }
}

運(yùn)行結(jié)果:

[main] INFO com.shiro.first.FirstShiro - My First Apache Shiro Application

Shiro概念

在沒有Shiro的時(shí)候譬重,我們在做項(xiàng)目中的登錄、權(quán)限之類的功能有五花八門的實(shí)現(xiàn)方式罐氨,不同系統(tǒng)的做法不統(tǒng)一臀规。但是有Shiro之后,大家就可以一致化地做權(quán)限系統(tǒng)栅隐,優(yōu)點(diǎn)就是各自的代碼不再晦澀難懂塔嬉,有一套統(tǒng)一的標(biāo)準(zhǔn)。另外Shiro框架也比較成熟租悄,能很好地滿足需求谨究。這就是我對(duì)Shiro的總結(jié)。

在Java SE中驗(yàn)證Shiro

Shiro不僅不依賴任何容器泣棋,可以在EE環(huán)境下運(yùn)行胶哲,也可以在SE環(huán)境下運(yùn)行,在快速入門中潭辈,我在SE環(huán)境下體驗(yàn)了Shiro的登錄驗(yàn)證鸯屿、角色驗(yàn)證、權(quán)限驗(yàn)證功能萎胰。

  • 配置文件方式

    • 在src目錄下創(chuàng)建shiro.ini文件碾盟,內(nèi)容如下:
      [users]
      #用戶 密碼 角色
    
      #博客管理員
      Object=123456,BlogManager
      #讀者
      Reader=654321,SimpleReader
    
      #定義各種角色
      [roles]
      #博客管理員權(quán)限
      BlogManager=addBlog,deleteBlog,modifyBlog,readBlog
      #普通讀者權(quán)限
      SimpleReader=readBlog,commentBlog
    
    • 創(chuàng)建用戶實(shí)體類
    /**
     * @author Object
     *  用戶實(shí)體類
     */
    public class User {
    
      private String name;
      private String password;
      public String getName() {
        return name;
      }
      public void setName(String name) {
        this.name = name;
      }
      public String getPassword() {
        return password;
      }
      public void setPassword(String password) {
        this.password = password;
      }
    
    }
    
    
    • 創(chuàng)建ShiroTest,驗(yàn)證登錄技竟、權(quán)限、角色:
      獲取當(dāng)前用戶
    /**
     * 獲取當(dāng)前用戶(Subject)
     * 
     * @param user
     * @return
     */
    public static Subject getSubject() {
        // 加載配置文件屈藐,獲取SecurityManager工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 從工廠中獲取SecurityManager對(duì)象
        SecurityManager securityManager = factory.getInstance();
        // 通過SecurityUtil將SecurityManager對(duì)象放入全局對(duì)象
        SecurityUtils.setSecurityManager(securityManager);
        // 全局對(duì)象通過SecurityManager生成Subject
        Subject subject = SecurityUtils.getSubject();
        return subject;
    }
    
    登錄:
    /**
     * 用戶登錄方法
     * 
     * @param user
     * @return
     */
    public static boolean login(User user) {
        Subject subject = getSubject();
        // 如果用戶已經(jīng)登錄 則退出
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        // 封裝用戶數(shù)據(jù)
        UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword());
        // 驗(yàn)證用戶數(shù)據(jù)
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            // 登錄失敗
            // e.printStackTrace();為了看結(jié)果榔组,暫時(shí)不讓它打印
            return false;
        }
        return subject.isAuthenticated();
    }
    
    判斷用戶是否為某個(gè)角色:
    /**
     * 判斷用戶是否擁有某個(gè)角色
     * 
     * @param user
     * @param role
     * @return
     */
    public static boolean hasRole(User user, String role) {
        Subject subject = getSubject();
        return subject.hasRole(role);
    }
    
    
    判斷用戶是否擁有某項(xiàng)權(quán)限
    /**
     * 判斷用戶是否擁有某種權(quán)限
     * 
     * @param user
     * @param permit
     * @return
     */
    public static boolean isPermit(User user, String permit) {
        Subject subject = getSubject();
        return subject.isPermitted(permit);
    }
    
    有了這四個(gè)方法,我們就可以開始寫測試類了联逻。我會(huì)創(chuàng)建兩個(gè)在配置文件中的用戶 —— Object and Reader 和一個(gè)不在配置文件中的用戶 —— Tom
        public static void main(String[] args) {
        // 用戶Object
        User object = new User();
        object.setName("Object");
        object.setPassword("123456");
    
        // 用戶Reader
        User reader = new User();
        reader.setName("Reader");
        // 錯(cuò)誤的密碼
        reader.setPassword("654321");
    
        // 不存在的用戶
        User tom = new User();
        tom.setName("Tom");
        tom.setPassword("123456");
    
        List<User> users = new LinkedList<User>();
        users.add(object);
        users.add(reader);
        users.add(tom);
    
        // 角色:BlogManager
        String blogManager = "BlogManager";
        // 角色:SimpleReader
        String simpleReader = "SimpleReader";
    
        List<String> roles = new LinkedList<String>();
        roles.add(blogManager);
        roles.add(simpleReader);
    
        // 權(quán)限
        String addBlog = "addBlog";
        String deleteBlog = "deleteBlog";
        String modifyBlog = "modifyBlog";
        String readBlog = "readBlog";
        String commentBlog = "commentBlog";
        List<String> permits = new LinkedList<String>();
        permits.add(addBlog);
        permits.add(deleteBlog);
        permits.add(modifyBlog);
        permits.add(readBlog);
        permits.add(commentBlog);
        /**************************** 開始驗(yàn)證 ****************************/
        System.out.println("=========================驗(yàn)證用戶是否登錄成功=========================");
        // 驗(yàn)證用戶是否登錄成功
        for (User u : users) {
            if (login(u)) {
                System.out.println("用戶:" + u.getName() + " 登錄成功  " + "密碼為:" + u.getPassword());
            } else {
                System.out.println("用戶:" + u.getName() + " 登錄失敗  " + "密碼為:" + u.getPassword());
            }
        }
        System.out.println("=========================驗(yàn)證用戶角色信息=========================");
        // 驗(yàn)證用戶角色
        for (User u : users) {
            for (String role : roles) {
                if (login(u)) {
                    if (hasRole(u, role)) {
                        System.out.println("用戶:" + u.getName() + " 的角色是" + role);
                    }
                }
            }
        }
        System.out.println("=========================驗(yàn)證用戶權(quán)限信息=========================");
        for(User u:users) {
            System.out.println("========================="+u.getName()+"權(quán)限=========================");
            for(String permit:permits) {
                if(login(u)) {
                    if(isPermit(u, permit)) {
                        System.out.println("用戶:"+u.getName() +" 有 "+permit+" 的權(quán)限 ");
                    }
                }
            }
        }
    }
    

    運(yùn)行結(jié)果如下(紅字是由于缺少部分jar搓扯,暫不解決):


    SE運(yùn)行結(jié)果

到這里為止,已經(jīng)完成了Shiro的入門包归。但是在實(shí)際項(xiàng)目中锨推,我們不可能用配置文件配置用戶權(quán)限,所以還是得結(jié)合數(shù)據(jù)庫進(jìn)行開發(fā)公壤。

Shiro結(jié)合數(shù)據(jù)庫

  • RABC概念

要結(jié)合數(shù)據(jù)庫進(jìn)行開發(fā)换可,得先理解一個(gè)概念 —— RABC

RBAC 是當(dāng)下權(quán)限系統(tǒng)的設(shè)計(jì)基礎(chǔ)厦幅,同時(shí)有兩種解釋:
一: Role-Based Access Control沾鳄,基于角色的訪問控制
即:你要能夠增刪改查博客确憨,那么當(dāng)前用戶就必須擁有博主這個(gè)角色译荞。
二:Resource-Based Access Control瓤的,基于資源的訪問控制
即吞歼,你要能夠讀博客圈膏、評(píng)論博客,那么當(dāng)前用戶就必須擁有讀者這樣的權(quán)限篙骡。

所以本辐,基于這個(gè)概念,我們的數(shù)據(jù)庫將有:用戶表医增、角色表慎皱、權(quán)限表、用戶——角色關(guān)系表叶骨、權(quán)限——角色關(guān)系表茫多,其中用戶角色關(guān)系為多對(duì)多,即一個(gè)用戶可以對(duì)應(yīng)多個(gè)角色忽刽,一個(gè)角色也可以由多個(gè)用戶扮演天揖,權(quán)限角色關(guān)系也為多對(duì)多,即一個(gè)角色可以有多個(gè)權(quán)限跪帝,一個(gè)權(quán)限也可以賦予多個(gè)角色今膊。

  • 需要的Jar包

連接SQL.png
  • 數(shù)據(jù)庫構(gòu)建

    我使用的是MySQL,創(chuàng)建語句如下:

    CREATE DATABASE shiro;
    USE shiro;
    CREATE TABLE user(
      id bigint primary key auto_increment,
        name varchar(16),
        password varchar(32)
    )charset=utf8 ENGINE=InnoDB;
    
    create table role (
      id bigint primary key auto_increment,
      name varchar(32)
    ) charset=utf8 ENGINE=InnoDB;
    
    create table permission (
      id bigint primary key auto_increment,
      name varchar(32)
    ) charset=utf8 ENGINE=InnoDB;
    
    create table user_role (
      uid bigint,
      rid bigint,
      constraint pk_users_roles primary key(uid, rid)
    ) charset=utf8 ENGINE=InnoDB;
    
    create table role_permission (
      rid bigint,
      pid bigint,
      constraint pk_roles_permissions primary key(rid, pid)
    ) charset=utf8 ENGINE=InnoDB;
    

    往數(shù)據(jù)庫中插入數(shù)據(jù):

    INSERT INTO `user` VALUES (1,'Object','123456');
    INSERT INTO `user` VALUES (2,'Reader','654321');
    INSERT INTO `user_role` VALUES (1,1);
    INSERT INTO `user_role` VALUES (2,2);
    INSERT INTO `role` VALUES (1,'blogManager');
    INSERT INTO `role` VALUES (2,'reader');
    INSERT INTO `permission` VALUES (1,'addBlog');
    INSERT INTO `permission` VALUES (2,'deleteBlog');
    INSERT INTO `permission` VALUES (3,'modifyBlog');
    INSERT INTO `permission` VALUES (4,'readBlog');
    INSERT INTO `permission` VALUES (5,'commentBlog');
    INSERT INTO `role_permission` VALUES (1,1);
    INSERT INTO `role_permission` VALUES (1,2);
    INSERT INTO `role_permission` VALUES (1,3);
    INSERT INTO `role_permission` VALUES (1,4);
    INSERT INTO `role_permission` VALUES (2,4);
    INSERT INTO `role_permission` VALUES (2,5);
    
  • 工程構(gòu)建

    • 創(chuàng)建實(shí)體類
    public class User {
        private int id;
        private String name;
        private String password;
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
    }
    
    • 修改shiro.ini配置文件
      因?yàn)槲覀円獜臄?shù)據(jù)庫中獲取用戶伞剑、角色斑唬、權(quán)限信息,所以就不需要在配置文件中配置了黎泣,但是我們要有一個(gè)Realm的概念恕刘,Realm在我的理解中,可以把它當(dāng)作一個(gè)橋梁抒倚,當(dāng)用戶(Subject)請(qǐng)求Realm時(shí)褐着,Realm就會(huì)去尋找ini配置文件或者數(shù)據(jù)庫中的用戶信息,Realm就是真正對(duì)用戶做驗(yàn)證的關(guān)卡托呕。我們要在DatabaseRealm中重寫兩個(gè)方法含蓉,分別是驗(yàn)證用戶授權(quán),那么Shiro怎么找到Realm呢项郊,就是靠shiro.ini配置文件馅扣。
    [main]
    databaseRealm=com.shirotest.DatabaseRealm
    securityManager.realms=$databaseRealm
    
    • 創(chuàng)建ShiroDao類
      這個(gè)類主要是從數(shù)據(jù)庫中取出用戶、權(quán)限列表呆抑、角色列表岂嗓。
      代碼如下:
    public class ShiroDao {
      private static Connection connection = null;
      private static PreparedStatement preparedStatement = null;
      static {
        try {
          Class.forName("com.mysql.jdbc.Driver");
          connection = DriverManager.getConnection(
              "jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC",
              "root", "971103");
        } catch (ClassNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    
      /**
       * 通過用戶名獲取密碼
       * 
       * @param username
       * @return
       */
      public static String getPassword(String username) {
        String sql = "select password from user where name = ?";
        ResultSet rs = null;
        try {
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setString(1, username);
          rs = preparedStatement.executeQuery();
          if (rs.next())
            return rs.getString("password");
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return null;
      }
    
      public static Set<String> getRoles(String username) {
        String sql = "select role.name "
            + "from role,user_role,user "
            + "where user.id=user_role.uid "
            + "and user_role.rid=role.id "
            + "and user.name = ?";
        ResultSet rs = null;
        Set<String> set = new HashSet<>();
        try {
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setString(1, username);
          rs = preparedStatement.executeQuery();
          while(rs.next()) {
            set.add(rs.getString("name"));
          }
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return set;
      }
    
      public static Set<String> getPermits(String username) {
        String sql = "select permission.name "
            + "from"
            + " permission,role_permission,role ,user_role,user "
            + "where "
            + "permission.id = role_permission.pid "
            + "and role_permission.rid = role.id "
            + "and role.id = user_role.rid "
            + "and user_role.uid = user.id "
            + "and user.name = ?";
        ResultSet rs = null;
        Set<String> set = new HashSet<>();
        try {
          preparedStatement = connection.prepareStatement(sql);
          preparedStatement.setString(1, username);
          rs = preparedStatement.executeQuery();
          while (rs.next()) {
            set.add(rs.getString("name"));
          }
        } catch (SQLException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return set;
      }
    
      public static void main(String[] args) {
        System.out.println("Object的角色:" + new ShiroDao().getRoles("Object"));
        System.out.println("Reader的角色:" + new ShiroDao().getRoles("Reader"));
        System.out.println("Object的權(quán)限:"+new ShiroDao().getPermits("Object"));
        System.out.println("Reader的權(quán)限:"+new ShiroDao().getPermits("Reader"));
      }
    }
    

    運(yùn)行結(jié)果:


    ShiroDao運(yùn)行結(jié)果
    • DatabaseRealm
    public class DatabaseRealm extends AuthorizingRealm{
    
      /**
       *授權(quán)的方法
       */
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //只有認(rèn)證成功了,Shiro才會(huì)調(diào)用這個(gè)方法進(jìn)行授權(quán)
        //1.獲取用戶
        String username = (String) principal.getPrimaryPrincipal();
        //2.獲取角色和權(quán)限列表
        Set<String> roles = ShiroDao.getRoles(username);
        Set<String> permissions = ShiroDao.getPermits(username);
        //3.授權(quán)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);
    
        return simpleAuthorizationInfo;
      }
    
      /**
       *驗(yàn)證用戶名密碼是否正確的方法
       */
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1.獲取用戶名密碼
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //獲取用戶名
        String username = usernamePasswordToken.getUsername();
        //獲取密碼
        String password = usernamePasswordToken.getPassword().toString();
        //獲取數(shù)據(jù)庫中的密碼
        String passwordInDatabase = ShiroDao.getPassword(username);
        //為空則表示沒有當(dāng)前用戶鹊碍,密碼不匹配表示密碼錯(cuò)誤
        if(null == passwordInDatabase||!password.equals(passwordInDatabase)) {
          throw new AuthenticationException();
        }
        //認(rèn)證信息:放用戶名密碼 getName()是父類的方法厌殉,返回當(dāng)前類名
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
        return simpleAuthenticationInfo;
      }
    }
    
    • 測試類TestShiro
    public class TestShiro {
      public static void main(String[] args) {
        // 用戶Object
          User object = new User();
          object.setName("Object");
          object.setPassword("123456");
    
          // 用戶Reader
          User reader = new User();
          reader.setName("Reader");
          // 錯(cuò)誤的密碼
          reader.setPassword("654321");
    
          // 不存在的用戶
          User tom = new User();
          tom.setName("Tom");
          tom.setPassword("123456");
    
          List<User> users = new LinkedList<User>();
          users.add(object);
          users.add(reader);
          users.add(tom);
    
          // 角色:BlogManager
          String blogManager = "blogManager";
          // 角色:SimpleReader
          String simpleReader = "reader";
    
          List<String> roles = new LinkedList<String>();
          roles.add(blogManager);
          roles.add(simpleReader);
    
          // 權(quán)限
          String addBlog = "addBlog";
          String deleteBlog = "deleteBlog";
          String modifyBlog = "modifyBlog";
          String readBlog = "readBlog";
          String commentBlog = "commentBlog";
          List<String> permits = new LinkedList<String>();
          permits.add(addBlog);
          permits.add(deleteBlog);
          permits.add(modifyBlog);
          permits.add(readBlog);
          permits.add(commentBlog);
          /**************************** 開始驗(yàn)證 ****************************/
          System.out.println("=========================驗(yàn)證用戶是否登錄成功=========================");
          // 驗(yàn)證用戶是否登錄成功
          for (User u : users) {
              if (login(u)) {
                  System.out.println("用戶:" + u.getName() + " 登錄成功  " + "密碼為:" + u.getPassword());
              } else {
                  System.out.println("用戶:" + u.getName() + " 登錄失敗  " + "密碼為:" + u.getPassword());
              }
          }
          System.out.println("=========================驗(yàn)證用戶角色信息=========================");
          // 驗(yàn)證用戶角色
          for (User u : users) {
              for (String role : roles) {
                  if (login(u)) {
                      if (hasRole(u, role)) {
                          System.out.println("用戶:" + u.getName() + " 的角色是" + role);
                      }
                  }
              }
          }
          System.out.println("=========================驗(yàn)證用戶權(quán)限信息=========================");
          for(User u:users) {
              System.out.println("========================="+u.getName()+"權(quán)限=========================");
              for(String permit:permits) {
                  if(login(u)) {
                      if(isPermitted(u, permit)) {
                          System.out.println("用戶:"+u.getName() +" 有 "+permit+" 的權(quán)限 ");
                      }
                  }
              }
          }
    
      }
      public static Subject getSubject() {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //獲取安全管理者實(shí)例
        SecurityManager sm = factory.getInstance();
        //將安全管理者放入全局對(duì)象
        SecurityUtils.setSecurityManager(sm);
        //全局對(duì)象通過安全管理者生成Subject對(duì)象
        Subject subject = SecurityUtils.getSubject();
        return subject;
      }
      public static boolean login(User user) {
        Subject subject = getSubject();
        if(subject.isAuthenticated()) {
          //如果登錄了食绿,就退出登錄
          subject.logout();
        }
        //封裝用戶數(shù)據(jù)
        AuthenticationToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
        try {
          subject.login(token);
        }catch(AuthenticationException e) {
          return false;
        }
        return subject.isAuthenticated();
      }
      private static boolean hasRole(User user, String role) {
        Subject subject = getSubject();
        return subject.hasRole(role);
      }
    
      private static boolean isPermitted(User user, String permit) {
        Subject subject = getSubject();
        return subject.isPermitted(permit);
      }
    }
    
    

    最終測試結(jié)果:


    最終測試結(jié)果

Shiro加密

我們在沒有Shiro的時(shí)候,也會(huì)使用各種加密算法來對(duì)用戶的密碼進(jìn)行加密公罕,Shiro框架也提供了自己的一套加密服務(wù)器紧,這里就說說MD5+鹽。

在不加鹽的MD5中楼眷,雖然密碼也是使用非對(duì)稱算法加密铲汪,同樣也不能回轉(zhuǎn)為明文,但是別人可以使用窮舉法列出最常用的密碼罐柳,例如12345 它加密后永遠(yuǎn)都是同一個(gè)密文掌腰,一些別有用心的人就可以通過這種常見密文得知你的密碼是12345。但是加鹽就不一樣张吉,他是在你的密碼原文的基礎(chǔ)上添加上一個(gè)隨機(jī)數(shù)齿梁,這個(gè)隨機(jī)數(shù)也會(huì)隨之保存在數(shù)據(jù)庫中,但是黑客拿到你的密碼之后他并不知道哪個(gè)隨機(jī)數(shù)是多少肮蛹,所以就很難再破譯密碼勺择。

操作一番。
首先要在數(shù)據(jù)庫中加一個(gè)"鹽"字段
ALTER TABLE user add column salt varchar(100)
同時(shí)在User實(shí)體類中加一個(gè)salt

  private String salt;
    public String getSalt() {
        return salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }

然后在ShiroDao中加一個(gè)注冊用戶的方法伦忠。

public static boolean registerUser(String username,String password) {
/***********************************Shiro加密***********************************/
        //獲取鹽值
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        //加密次數(shù)
        int times = 3;
        //加密方式
        String type = "md5";
        //加密后的最終密碼
        String lastPassword = new SimpleHash(type, password, salt, times).toString();
/***********************************加密結(jié)束***********************************/
    
        String sql = "INSERT INTO user(name,password,salt)VALUES(?,?,?)";
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, username);
            preparedStatement.setString(2, lastPassword);
            preparedStatement.setString(3, salt);
            if(preparedStatement.execute()) {
                return true;
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
        return false;
    }

同時(shí)加一個(gè)獲取用戶的方法:

public static User getUser(String username) {
        String sql = "select * from user where name = ?";
        User user = new User();
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, username);
            ResultSet resultSet = preparedStatement.executeQuery();
            while(resultSet.next()) {
                user.setName(resultSet.getString("name"));
                user.setPassword(resultSet.getString("password"));
                user.setSalt(resultSet.getString("salt"));
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return user;
        
    }

修改之前的DatabaseRealm類中的驗(yàn)證用戶方法省核,加一個(gè)將用戶輸入的密碼加密后與數(shù)據(jù)庫中密碼進(jìn)行比對(duì)的邏輯。具體邏輯如下:

//1.獲取用戶名密碼
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//獲取用戶名
String username = usernamePasswordToken.getUsername();
//獲取密碼
String password = new String(usernamePasswordToken.getPassword());
System.out.println("明文密碼:"+password);
//獲取數(shù)據(jù)庫中的用戶
User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
//String passwordInDatabase = ShiroDao.getPassword(username);
        
        
//將用戶輸入的密碼做一個(gè)加密后與數(shù)據(jù)庫中的進(jìn)行比對(duì)
String passwordMd5 = new SimpleHash("md5", password, user.getSalt(), 3).toString();
System.out.println("salt:"+user.getSalt());
System.out.println("密文密碼:"+passwordMd5);
System.out.println("正在驗(yàn)證中......");
//為空則表示沒有當(dāng)前用戶昆码,密碼不匹配表示密碼錯(cuò)誤
if(null == user.getPassword()||!passwordMd5.equals(user.getPassword())) {
    throw new AuthenticationException();
}
//認(rèn)證信息:放用戶名密碼 getName9()是父類的方法气忠,返回當(dāng)前類名
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
return simpleAuthenticationInfo;

main測試:

    ShiroDao.registerUser("Object2", "321321");
        User object2 = new User();
        object2.setName("Object2");
        object2.setPassword("321321");
        if (login(object2)) {
            System.out.println("登錄成功");
        } else {
            System.out.println("登錄失敗");
        }

最后結(jié)果:

加密結(jié)果

數(shù)據(jù)庫結(jié)果:

數(shù)據(jù)庫結(jié)果

第二種驗(yàn)證用戶的方式

剛才我們是在doGetAuthenticationInfo方法中自己寫了驗(yàn)證邏輯,再來捋一遍:

1.獲取用戶輸入的密碼
2.獲取數(shù)據(jù)庫中該用戶的鹽
3.將用戶輸入的密碼進(jìn)行加鹽加密
4.將加密后的密碼和數(shù)據(jù)庫中的密碼進(jìn)行比對(duì)

大概是要經(jīng)歷這么多步驟吧未桥。其實(shí)Shiro提供了一個(gè)HashedCredentialsMatcher 笔刹,可以自動(dòng)幫我們做這些工作。

步驟:
1.修改配置文件

[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5 #加密方式
credentialsMatcher.hashIterations=3 #剛才我們指定的加密次數(shù)
credentialsMatcher.storedCredentialsHexEncoded=true

databaseRealm=com.shirotest.DatabaseRealm
securityManager.realms=$databaseRealm

2.修改doGetAuthenticationInfo方法

//1.獲取用戶名密碼
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //獲取用戶名
        String username = usernamePasswordToken.getUsername();
        //獲取密碼
        String password = new String(usernamePasswordToken.getPassword());
        System.out.println("明文密碼:"+password);
        //獲取數(shù)據(jù)庫中的用戶
        User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
        //String passwordInDatabase = ShiroDao.getPassword(username);
        
        
        //將用戶輸入的密碼做一個(gè)加密后與數(shù)據(jù)庫中的進(jìn)行比對(duì)
        System.out.println("數(shù)據(jù)庫中密碼:"+user.getPassword());
        String passwordMd5 = new SimpleHash("md5", password, user.getSalt(), 3).toString();
        System.out.println("salt:"+user.getSalt());
        System.out.println("密文密碼:"+passwordMd5);
        System.out.println("正在驗(yàn)證中......");
        /*
         * //為空則表示沒有當(dāng)前用戶冬耿,密碼不匹配表示密碼錯(cuò)誤 if(null ==
         * user.getPassword()||!passwordMd5.equals(user.getPassword())) { throw new
         * AuthenticationException(); }
         */
        //認(rèn)證信息:放用戶名密碼 getName9()是父類的方法,返回當(dāng)前類名
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());
        return simpleAuthenticationInfo;

主要是修改了驗(yàn)證信息萌壳,將數(shù)據(jù)庫中的密碼和鹽傳入亦镶,讓它自行判斷,我們就無需再寫判斷邏輯了袱瓮。SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());

運(yùn)行結(jié)果:


第二種方式運(yùn)行結(jié)果

小結(jié)

到這里為止缤骨,Shiro關(guān)于SE的部分應(yīng)該就告一段落了,之后要開始學(xué)習(xí)關(guān)于集成Web和集成框架了尺借,我覺得對(duì)于Shiro的架構(gòu)及原理绊起,得單獨(dú)瀏覽一遍,因?yàn)榈酱藶橹刮乙仓恢繱hiro是怎么使用的燎斩,但是其中Realm類中的那兩個(gè)方法虱歪,何時(shí)調(diào)用蜂绎,為什么會(huì)調(diào)用,還有SimpleAuthenticationInfo返回后是怎么判斷登錄成功或者失敗的笋鄙,可以說是很模糊师枣,學(xué)完集成框架后我應(yīng)該會(huì)選擇再看看其中的原理。

歡迎大家訪問我的個(gè)人博客:Object's Blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萧落,一起剝皮案震驚了整個(gè)濱河市践美,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌找岖,老刑警劉巖陨倡,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異许布,居然都是意外死亡兴革,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門爹脾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帖旨,“玉大人,你說我怎么就攤上這事灵妨〗庠模” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵泌霍,是天一觀的道長货抄。 經(jīng)常有香客問我,道長朱转,這世上最難降的妖魔是什么蟹地? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮藤为,結(jié)果婚禮上怪与,老公的妹妹穿的比我還像新娘。我一直安慰自己缅疟,他們只是感情好分别,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著存淫,像睡著了一般耘斩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上桅咆,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天括授,我揣著相機(jī)與錄音,去河邊找鬼。 笑死荚虚,一個(gè)胖子當(dāng)著我的面吹牛薛夜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播曲管,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼却邓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了院水?” 一聲冷哼從身側(cè)響起腊徙,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檬某,沒想到半個(gè)月后撬腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恢恼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年民傻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片场斑。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漓踢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出漏隐,到底是詐尸還是另有隱情喧半,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布青责,位于F島的核電站挺据,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏脖隶。R本人自食惡果不足惜扁耐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望产阱。 院中可真熱鬧婉称,春花似錦、人聲如沸构蹬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怎燥。三九已至,卻和暖如春蜜暑,著一層夾襖步出監(jiān)牢的瞬間铐姚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隐绵,地道東北人之众。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像依许,于是被迫代替她去往敵國和親棺禾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353