一 加密
md5:非对称加密,既不可逆的
缺陷:相同的明文密码对应的md5加密结果相同,这样穷举法就可以破解
解决办法:盐 和 加密次数。这些都需要保存下来,不同的用户盐也不同
自定义Realm
DAO
package com.how2java;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
/**
* @author zhuyc
* @create 2019-06-22 8:14
*/
public class UserDao {
public UserDao() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8", "root",
"123456");
}
public String createUser(String name, String password) {
String sql = "insert into user values(null,?,?,?)";
String salt = new SecureRandomNumberGenerator().nextBytes().toString(); //盐量随机
String encodedPassword= new SimpleHash("md5",password,salt,2).toString();
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, name);
ps.setString(2, encodedPassword);
ps.setString(3, salt);
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public String getPassword(String userName) {
String sql = "select password from user where name = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, userName);
ResultSet rs = ps.executeQuery();
if (rs.next())
return rs.getString("password");
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public User getUser(String userName) {
User user = null;
String sql = "select * from user where name = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, userName);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setSalt(rs.getString("salt"));
}
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
public Set<String> listRoles(String userName) {
Set<String> roles = new HashSet<>();
String sql = "select r.name from user u "
+ "left join user_role ur on u.id = ur.uid "
+ "left join Role r on r.id = ur.rid "
+ "where u.name = ?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, userName);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
roles.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return roles;
}
public Set<String> listPermissions(String userName) {
Set<String> permissions = new HashSet<>();
String sql =
"select p.name from user u "+
"left join user_role ru on u.id = ru.uid "+
"left join role r on r.id = ru.rid "+
"left join role_permission rp on r.id = rp.rid "+
"left join permission p on p.id = rp.pid "+
"where u.name =?";
try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
ps.setString(1, userName);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
permissions.add(rs.getString(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
return permissions;
}
}
Realm
package com.how2java;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class DatabaseRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//能进入到这里,表示账号已经通过验证了
String userName =(String) principalCollection.getPrimaryPrincipal();
//通过DAO获取角色和权限
Set<String> permissions = new UserDao().listPermissions(userName);
Set<String> roles = new UserDao().listRoles(userName);
//授权对象
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
//把通过DAO获取到的角色和权限放进去
s.setStringPermissions(permissions);
s.setRoles(roles);
return s;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String userName= token.getPrincipal().toString();
String password =new String(t.getPassword());
//获取数据库中的密码
User user = new UserDao().getUser(userName);
String passwordInDB = user.getPassword();
String salt = user.getSalt();
String passwordEncoded = new SimpleHash("md5",password,salt,2).toString();
if(null==user || !passwordEncoded.equals(passwordInDB))
throw new AuthenticationException();
//认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName());
return a;
}
}
配置realm
[main]
databaseRealm=com.how2java.DatabaseRealm
securityManager.realms=$databaseRealm
测试类
package com.how2java;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/**
* @author zhuyc
* @create 2019-06-22 21:22
*/
public class TestShiroEncoder {
public static void main(String[] args) {
//这里要释放注释,先注册一个用户
// new UserDao().createUser("tom", "123");
User user = new User();
user.setName("tom");
user.setPassword("123");
if(login(user))
System.out.println("登录成功");
else
System.out.println("登录失败");
}
private static boolean hasRole(User user, String role) {
Subject subject = getSubject(user);
return subject.hasRole(role);
}
private static boolean isPermitted(User user, String permit) {
Subject subject = getSubject(user);
return subject.isPermitted(permit);
}
private static Subject getSubject(User user) {
//加载配置文件,并获取工厂
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//获取安全管理者实例
SecurityManager sm = factory.getInstance();
//将安全管理者放入全局对象
SecurityUtils.setSecurityManager(sm);
//全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
return subject;
}
private static boolean login(User user) {
Subject subject= getSubject(user);
//如果已经登录过了,退出
if(subject.isAuthenticated())
subject.logout();
//封装用户的数据
UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword());
try {
//将用户的数据token 最终传递到Realm中进行对比
subject.login(token);
} catch (AuthenticationException e) {
//验证错误
return false;
}
return subject.isAuthenticated();
}
}
HashedCredentialsMatcher
上面的做法是做法是自己计算加密后的秘文,再自己比较。 另一个做法是使用Shiro提供的 HashedCredentialsMatcher
帮我们做。
修改Realm
其他不改,这里直接放数据库中的加密密码。也不需要我们去比较
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println(this.getCredentialsMatcher());
//获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String userName= token.getPrincipal().toString();
//获取数据库中的密码
User user = new DAO().getUser(userName);
String passwordInDB = user.getPassword();
String salt = user.getSalt();
//认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
//盐也放进去
//这样通过shiro.ini里配置的 HashedCredentialsMatcher 进行自动校验
SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,passwordInDB,ByteSource.Util.bytes(salt),getName());
return a;
}
修改shiro.ini
为DatabaseRealm 指定credentialsMatcher
,其中就指定了算法是 md5, 次数为2, storedCredentialsHexEncoded 这个表示计算之后以密文为16进制。
[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2
credentialsMatcher.storedCredentialsHexEncoded=true
databaseRealm=com.how2java.DatabaseRealm
databaseRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$databaseRealm
网友评论