前言
由于最近在做一個(gè)項(xiàng)目弟劲,剛完成到登錄注冊,不想和以前的項(xiàng)目搬同樣的磚了镶摘,想完成點(diǎn)不那么low的功能嗽桩,像單點(diǎn)登錄、權(quán)限控制等凄敢,于是就想起了Shiro框架碌冶。
初識(shí)Shiro
任何一種技術(shù)總有個(gè)開始,又總是這么巧涝缝,每個(gè)開始總是個(gè)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);
}
}
運(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; }
判斷用戶是否為某個(gè)角色:/** * 用戶登錄方法 * * @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(); }
判斷用戶是否擁有某項(xiàng)權(quán)限/** * 判斷用戶是否擁有某個(gè)角色 * * @param user * @param role * @return */ public static boolean hasRole(User user, String role) { Subject subject = getSubject(); return subject.hasRole(role); }
有了這四個(gè)方法,我們就可以開始寫測試類了联逻。我會(huì)創(chuàng)建兩個(gè)在配置文件中的用戶 —— Object and Reader 和一個(gè)不在配置文件中的用戶 —— Tom/** * 判斷用戶是否擁有某種權(quán)限 * * @param user * @param permit * @return */ public static boolean isPermit(User user, String permit) { Subject subject = getSubject(); return subject.isPermitted(permit); }
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搓扯,暫不解決):
到這里為止,已經(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包
-
數(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é)果:
- 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é)果:
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é)果:
數(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é)果:
小結(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