1. 程式人生 > 程式設計 >關於Shiro框架的學習(一)

關於Shiro框架的學習(一)

前言

由於最近在做一個專案,剛完成到登入註冊,不想和以前的專案搬同樣的磚了,想完成點不那麼low的功能,像單點登入、許可權控制等,於是就想起了Shiro框架。

初識Shiro

任何一種技術總有個開始,又總是這麼巧,每個開始總是個HelloWorld。 官方給出的依賴:

依賴

示例程式碼:

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); } } 複製程式碼

執行結果:

[main] INFO com.shiro.first.FirstShiro - My First Apache Shiro Application
複製程式碼

Shiro概念

在沒有Shiro的時候,我們在做專案中的登入、許可權之類的功能有五花八門的實現方式,不同系統的做法不統一。但是有Shiro之後,大家就可以一致化地做許可權系統,優點就是各自的程式碼不再晦澀難懂,有一套統一的標準。另外Shiro框架也比較成熟,能很好地滿足需求。這就是我對Shiro的總結。

在Java SE中驗證Shiro

Shiro不僅不依賴任何容器,可以在EE環境下執行,也可以在SE環境下執行,在快速入門中,我在SE環境下體驗了Shiro的登入驗證、角色驗證、許可權驗證功能。

  • 配置檔案方式

    • 在src目錄下建立shiro.ini檔案,內容如下:
      [users]
      #使用者 密碼 角色
    
      #部落格管理員
      Object=123456,BlogManager
      #讀者
      Reader=654321,SimpleReader
    
      #定義各種角色
      [roles]
      #部落格管理員許可權
      BlogManager=addBlog,deleteBlog,modifyBlog,readBlog
      #普通讀者許可權
      SimpleReader=readBlog,commentBlog
    複製程式碼
    • 建立使用者實體類
    /**
     * @author Object
     * 	使用者實體類
     */
    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; } } 複製程式碼
    • 建立ShiroTest,驗證登入、許可權、角色: 獲取當前使用者
    /**
     * 獲取當前使用者(Subject)
     * 
     * @param user
     * @return
     */
    public static Subject getSubject() {
    	// 載入配置檔案,獲取SecurityManager工廠
    	Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    	// 從工廠中獲取SecurityManager物件
    	SecurityManager securityManager = factory.getInstance();
    	// 通過SecurityUtil將SecurityManager物件放入全域性物件
    	SecurityUtils.setSecurityManager(securityManager);
    	// 全域性物件通過SecurityManager生成Subject
    	Subject subject = SecurityUtils.getSubject();
    	return subject;
    }
    複製程式碼
    登入:
    /**
     * 使用者登入方法
     * 
     * @param user
     * @return
     */
    public static boolean login(User user) {
    	Subject subject = getSubject();
    	// 如果使用者已經登入 則退出
    	if (subject.isAuthenticated()) {
    		subject.logout();
    	}
    	// 封裝使用者資料
    	UsernamePasswordToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
    	// 驗證使用者資料
    	try {
    		subject.login(token);
    	} catch (AuthenticationException e) {
    		// 登入失敗
    		// e.printStackTrace();為了看結果,暫時不讓它列印
    		return false;
    	}
    	return subject.isAuthenticated();
    }
    複製程式碼
    判斷使用者是否為某個角色:
    /**
     * 判斷使用者是否擁有某個角色
     * 
     * @param user
     * @param role
     * @return
     */
    public static boolean hasRole(User user,String role) {
    	Subject subject = getSubject();
    	return subject.hasRole(role);
    }
    
    複製程式碼
    判斷使用者是否擁有某項許可權
    /**
     * 判斷使用者是否擁有某種許可權
     * 
     * @param user
     * @param permit
     * @return
     */
    public static boolean isPermit(User user,String permit) {
    	Subject subject = getSubject();
    	return subject.isPermitted(permit);
    }
    複製程式碼
    有了這四個方法,我們就可以開始寫測試類了。我會建立兩個在配置檔案中的使用者 —— Object and Reader 和一個不在配置檔案中的使用者 —— 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");
    	// 錯誤的密碼
    	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);
    
    	// 許可權
    	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);
    	/**************************** 開始驗證 ****************************/
    	System.out.println("=========================驗證使用者是否登入成功=========================");
    	// 驗證使用者是否登入成功
    	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("=========================驗證使用者角色資訊=========================");
    	// 驗證使用者角色
    	for (User u : users) {
    		for (String role : roles) {
    			if (login(u)) {
    				if (hasRole(u,role)) {
    					System.out.println("使用者:" + u.getName() + " 的角色是" + role);
    				}
    			}
    		}
    	}
    	System.out.println("=========================驗證使用者許可權資訊=========================");
    	for(User u:users) {
    		System.out.println("========================="+u.getName()+"許可權=========================");
    		for(String permit:permits) {
    			if(login(u)) {
    				if(isPermit(u,permit)) {
    					System.out.println("使用者:"+u.getName() +" 有 "+permit+" 的許可權 ");
    				}
    			}
    		}
    	}
    }
    複製程式碼
    執行結果如下(紅字是由於缺少部分jar,暫不解決):

SE執行結果

到這裡為止,已經完成了Shiro的入門。但是在實際專案中,我們不可能用配置檔案配置使用者許可權,所以還是得結合資料庫進行開發。

Shiro結合資料庫

  • RABC概念

要結合資料庫進行開發,得先理解一個概念 —— RABC

RBAC 是當下許可權系統的設計基礎,同時有兩種解釋: 一: Role-Based Access Control,基於角色的訪問控制。 即:你要能夠增刪改查部落格,那麼當前使用者就必須擁有博主這個角色。 二:Resource-Based Access Control,基於資源的訪問控制。 即,你要能夠讀部落格、評論部落格,那麼當前使用者就必須擁有讀者這樣的許可權。

所以,基於這個概念,我們的資料庫將有:使用者表、角色表、許可權表、使用者——角色關係表、許可權——角色關係表,其中使用者角色關係為多對多,即一個使用者可以對應多個角色,一個角色也可以由多個使用者扮演,許可權角色關係也為多對多,即一個角色可以有多個許可權,一個許可權也可以賦予多個角色。

  • 需要的Jar包

jar

  • 資料庫構建

    我使用的是MySQL,建立語句如下:

    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;
    複製程式碼

    往資料庫中插入資料:

    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,5);
    複製程式碼
  • 工程構建

    • 建立實體類
    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配置檔案 因為我們要從資料庫中獲取使用者、角色、許可權資訊,所以就不需要在配置檔案中配置了,但是我們要有一個Realm的概念,Realm在我的理解中,可以把它當作一個橋樑,當使用者(Subject)請求Realm時,Realm就會去尋找ini配置檔案或者資料庫中的使用者資訊,Realm就是真正對使用者做驗證的關卡。我們要在DatabaseRealm中重寫兩個方法,分別是驗證使用者授權,那麼Shiro怎麼找到Realm呢,就是靠shiro.ini配置檔案。
    [main]
    databaseRealm=com.shirotest.DatabaseRealm
    securityManager.realms=$databaseRealm
    複製程式碼
    • 建立ShiroDao類 這個類主要是從資料庫中取出使用者、許可權列表、角色列表。 程式碼如下:
    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 "
            + "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的許可權:"+new ShiroDao().getPermits("Object"));
        System.out.println("Reader的許可權:"+new ShiroDao().getPermits("Reader"));
      }
    }
    複製程式碼

    執行結果:

    ShiroDao執行結果

    • DatabaseRealm
        public class DatabaseRealm extends AuthorizingRealm{
    
          /**
           *授權的方法
           */
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
            //只有認證成功了,Shiro才會呼叫這個方法進行授權
            //1.獲取使用者
            String username = (String) principal.getPrimaryPrincipal();
            //2.獲取角色和許可權列表
            Set<String> roles = ShiroDao.getRoles(username);
            Set<String> permissions = ShiroDao.getPermits(username);
            //3.授權
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.setRoles(roles);
            simpleAuthorizationInfo.setStringPermissions(permissions);
    
            return simpleAuthorizationInfo;
          }
    
          /**
           *驗證使用者名稱密碼是否正確的方法
           */
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //1.獲取使用者名稱密碼
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            //獲取使用者名稱
            String username = usernamePasswordToken.getUsername();
            //獲取密碼
            String password = usernamePasswordToken.getPassword().toString();
            //獲取資料庫中的密碼
            String passwordInDatabase = ShiroDao.getPassword(username);
            //為空則表示沒有當前使用者,密碼不匹配表示密碼錯誤
            if(null == passwordInDatabase||!password.equals(passwordInDatabase)) {
              throw new AuthenticationException();
            }
            //認證資訊:放使用者名稱密碼 getName()是父類的方法,返回當前類名
            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");
          // 錯誤的密碼
          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);
    
          // 許可權
          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);
          /**************************** 開始驗證 ****************************/
          System.out.println("=========================驗證使用者是否登入成功=========================");
          // 驗證使用者是否登入成功
          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("=========================驗證使用者角色資訊=========================");
          // 驗證使用者角色
          for (User u : users) {
              for (String role : roles) {
                  if (login(u)) {
                      if (hasRole(u,role)) {
                          System.out.println("使用者:" + u.getName() + " 的角色是" + role);
                      }
                  }
              }
          }
          System.out.println("=========================驗證使用者許可權資訊=========================");
          for(User u:users) {
              System.out.println("========================="+u.getName()+"許可權=========================");
              for(String permit:permits) {
                  if(login(u)) {
                      if(isPermitted(u,permit)) {
                          System.out.println("使用者:"+u.getName() +" 有 "+permit+" 的許可權 ");
                      }
                  }
              }
          }
    
      }
      public static Subject getSubject() {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //獲取安全管理者例項
        SecurityManager sm = factory.getInstance();
        //將安全管理者放入全域性物件
        SecurityUtils.setSecurityManager(sm);
        //全域性物件通過安全管理者生成Subject物件
        Subject subject = SecurityUtils.getSubject();
        return subject;
      }
      public static boolean login(User user) {
        Subject subject = getSubject();
        if(subject.isAuthenticated()) {
          //如果登入了,就退出登入
          subject.logout();
        }
        //封裝使用者資料
        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);
      }
    }
    
    複製程式碼

    最終測試結果:

最終測試結果

Shiro加密

我們在沒有Shiro的時候,也會使用各種加密演演算法來對使用者的密碼進行加密,Shiro框架也提供了自己的一套加密服務,這裡就說說MD5+鹽。

在不加鹽的MD5中,雖然密碼也是使用非對稱演演算法加密,同樣也不能迴轉為明文,但是別人可以使用窮舉法列出最常用的密碼,例如12345 它加密後永遠都是同一個密文,一些別有用心的人就可以通過這種常見密文得知你的密碼是12345。但是加鹽就不一樣,他是在你的密碼原文的基礎上新增上一個隨機數,這個隨機數也會隨之儲存在資料庫中,但是黑客拿到你的密碼之後他並不知道哪個隨機數是多少,所以就很難再破譯密碼。

操作一番。 首先要在資料庫中加一個"鹽"欄位 ALTER TABLE user add column salt varchar(100) 同時在User實體類中加一個salt

  private String salt;
	public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
複製程式碼

然後在ShiroDao中加一個註冊使用者的方法。

public static boolean registerUser(String username,String password) {
/***********************************Shiro加密***********************************/
		//獲取鹽值
		String salt = new SecureRandomNumberGenerator().nextBytes().toString();
		//加密次數
		int times = 3;
		//加密方式
		String type = "md5";
		//加密後的最終密碼
		String lastPassword = new SimpleHash(type,salt,times).toString();
/***********************************加密結束***********************************/
    
		String sql = "INSERT INTO user(name,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;
	}
複製程式碼

同時加一個獲取使用者的方法:

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類中的驗證使用者方法,加一個將使用者輸入的密碼加密後與資料庫中密碼進行比對的邏輯。具體邏輯如下:

//1.獲取使用者名稱密碼
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//獲取使用者名稱
String username = usernamePasswordToken.getUsername();
//獲取密碼
String password = new String(usernamePasswordToken.getPassword());
System.out.println("明文密碼:"+password);
//獲取資料庫中的使用者
User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
//String passwordInDatabase = ShiroDao.getPassword(username);
		
		
//將使用者輸入的密碼做一個加密後與資料庫中的進行比對
String passwordMd5 = new SimpleHash("md5",user.getSalt(),3).toString();
System.out.println("salt:"+user.getSalt());
System.out.println("密文密碼:"+passwordMd5);
System.out.println("正在驗證中......");
//為空則表示沒有當前使用者,密碼不匹配表示密碼錯誤
if(null == user.getPassword()||!passwordMd5.equals(user.getPassword())) {
	throw new AuthenticationException();
}
//認證資訊:放使用者名稱密碼 getName9()是父類的方法,返回當前類名
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,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("登入失敗");
		}
複製程式碼

最後結果:

加密結果

資料庫結果:

資料庫結果

第二種驗證使用者的方式

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

1.獲取使用者輸入的密碼
2.獲取資料庫中該使用者的鹽
3.將使用者輸入的密碼進行加鹽加密
4.將加密後的密碼和資料庫中的密碼進行比對
複製程式碼

大概是要經歷這麼多步驟吧。其實Shiro提供了一個HashedCredentialsMatcher ,可以自動幫我們做這些工作。

步驟: 1.修改配置檔案

[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5 #加密方式
credentialsMatcher.hashIterations=3 #剛才我們指定的加密次數
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);
		//獲取資料庫中的使用者
		User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
		//String passwordInDatabase = ShiroDao.getPassword(username);
		
		
		//將使用者輸入的密碼做一個加密後與資料庫中的進行比對
		System.out.println("資料庫中密碼:"+user.getPassword());
		String passwordMd5 = new SimpleHash("md5",3).toString();
		System.out.println("salt:"+user.getSalt());
		System.out.println("密文密碼:"+passwordMd5);
		System.out.println("正在驗證中......");
		/*
		 * //為空則表示沒有當前使用者,密碼不匹配表示密碼錯誤 if(null ==
		 * user.getPassword()||!passwordMd5.equals(user.getPassword())) { throw new
		 * AuthenticationException(); }
		 */
		//認證資訊:放使用者名稱密碼 getName9()是父類的方法,返回當前類名
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());
		return simpleAuthenticationInfo;
複製程式碼

主要是修改了驗證資訊,將資料庫中的密碼和鹽傳入,讓它自行判斷,我們就無需再寫判斷邏輯了。SimpleAuthenticationInfo(username,getName());

執行結果:

第二種方式執行結果

小結

到這裡為止,Shiro關於SE的部分應該就告一段落了,之後要開始學習關於整合Web和整合框架了,我覺得對於Shiro的架構及原理,得單獨瀏覽一遍,因為到此為止我也只知道Shiro是怎麼使用的,但是其中Realm類中的那兩個方法,何時呼叫,為什麼會呼叫,還有SimpleAuthenticationInfo返回後是怎麼判斷登入成功或者失敗的,可以說是很模糊,學完整合框架後我應該會選擇再看看其中的原理。

歡迎大家訪問我的個人部落格:Object's Blog