關於Shiro框架的學習(一)

前言

因爲最近在作一個項目,剛完成到登陸註冊,不想和之前的項目搬一樣的磚了,想完成點不那麼low的功能,像單點登陸、權限控制等,因而就想起了Shiro框架。mysql

初識Shiro

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

依賴

示例代碼:sql

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的總結。apache

在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的入門。可是在實際項目中,咱們不可能用配置文件配置用戶權限,因此仍是得結合數據庫進行開發。bash

Shiro結合數據庫

  • RABC概念

要結合數據庫進行開發,得先理解一個概念 —— RABC架構

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

因此,基於這個概念,咱們的數據庫將有:用戶表、角色表、權限表、用戶——角色關係表、權限——角色關係表,其中用戶角色關係爲多對多,即一個用戶能夠對應多個角色,一個角色也能夠由多個用戶扮演,權限角色關係也爲多對多,即一個角色能夠有多個權限,一個權限也能夠賦予多個角色。dom

  • 須要的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,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_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, password, salt, times).toString();
/***********************************加密結束***********************************/
    
		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;
	}
複製代碼

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

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", password, 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,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("登陸失敗");
		}
複製代碼

最後結果:

加密結果

數據庫結果:

數據庫結果

第二種驗證用戶的方式

剛纔咱們是在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", password, 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,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());
		return simpleAuthenticationInfo;
複製代碼

主要是修改了驗證信息,將數據庫中的密碼和鹽傳入,讓它自行判斷,咱們就無需再寫判斷邏輯了。SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());

運行結果:

第二種方式運行結果

小結

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

歡迎你們訪問個人我的博客:Object's Blog

相關文章
相關標籤/搜索