目录
认证和策略
简介
针对多个reaml,可以进行realm个数进行配置
默认有三个策略
- AllSuccessfulStrategy
所有认证成功 - AtLeastOneSuccessfulStrategy (默认)
最少有一个Realm验证成功即可,返回所有Realm身份验证成功的验证消息。与FirstSuccessfulStrategy区别在于它只返回一个认证成功的数据。 - FirstSuccessfulStrategy,返回第一个Realm身份验证成功的消息。
main:
public static void main(String[] args) {
Subject subject= ShiroUtils.getSubject("classpath:shiro03/shiro.ini");
// 第四步登录
UsernamePasswordToken token=new UsernamePasswordToken("admin","111");
//第五步 身份认证 认证成功返回true,认证失败抛出异常
try {
//认证前
// System.out.println("认证之前-是否认证:"+subject.isAuthenticated());
subject.login(token);
// System.out.println("认证之后-是否认证:"+subject.isAuthenticated());
// System.out.println("调用logout");
// subject.logout();
System.out.println("是否认证:"+subject.isAuthenticated());
} catch (UnknownAccountException e) {
System.out.println("账户异常:"+e.getMessage());
}catch (IncorrectCredentialsException e)
{
System.out.println("密码异常:"+e.getMessage());
}catch (AuthenticationException e){
System.out.println(e.getMessage());
}
PrincipalCollection principals = subject.getPrincipals();
System.out.println("验证成功的用户名:"+principals+" 验证成功的realm:"+principals.getRealmNames());
}
user.properties
user.admin=111
user.tom=666
shiro.ini
#配置数据源
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicde=false&characterEncoding=utf-8
dataSource.username=root
dataSource.password=123456
# 使用JdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#注入数据源
jdbcRealm.dataSource=$dataSource
#重写认证sql语句
jdbcRealm.authenticationQuery=select password from t_user where login_name=?
# 使用自定义Reaml
propertiesRealm= shiro02.realm.PropertiesRealm
propertiesRealm.path=classpath:shiro02/user.properties
#认证器
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
# 认证策略
# 第一种: 最少有一个Realm验证成功即可,返回所有Realm身份验证成功的验证消息
authenticationStrategy=org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy
# 第二种: 最少有一个Realm验证成功即可,返回第一个Realm身份验证成功的消息。
authenticationStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy
# 第三种: 必须所有Realm认证成功。
authenticationStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
authenticator.authenticationStrategy=$authenticationStrategy
#将认证器添加到securityManager
securityManager.authenticator=$authenticator
securityManager.realms=$jdbcRealm,$propertiesRealm
sql 语句
drop database if exists shiro;
create database shiro charset utf8 ;
use shiro;
create table t_user
(
id int primary key auto_increment,
login_name varchar(200) not null unique,
password varchar(200)
)engine=Innodb charset utf8;
insert into t_user(login_name,password)values('admin','111');
insert into t_user(login_name,password)values('tom','222');
密码加密
编码/解码
两种方式 base64和十六进制
- base64
public void test01(){
//编码
String str="hello shrio";
String encodeToString = Base64.encodeToString(str.getBytes());
System.out.println(encodeToString);
//解码
String decodeToString = Base64.decodeToString(encodeToString);
System.out.println(decodeToString);
}
- 十六进制
@Test
public void test2()
{
String str2="hello Hex";
String encodingToString= org.apache.shiro.codec.Hex.encodeToString(str2.getBytes());
System.out.println(encodingToString);
String decodeToString=new String(Hex.decode(encodingToString));
System.out.println(decodeToString);
}
散列加密
- md5
String str="hello md5";
//普通MD5 安全一般
String str1=new Md5Hash(str).toString();
System.out.println(str1);
//带盐值加密 安全中等
String salt="saltHello";
String str2=new Md5Hash(str,salt).toBase64();
System.out.println(str2);
//盐值散列次数 非常安全
String str3=new Md5Hash(str,salt,3).toBase64();
System.out.println(str3);
SHA 加密
//三种方式
String username="toms";
String password="123";
String s1=new Sha1Hash(password,username,3).toHex();
String s2=new Sha256Hash(password,username,3).toHex();
String s3=new Sha512Hash(password,username,3).toHex();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
shiro 加密工具类
SimpleHash
String password="zhangsan123";
String salt="salt";
String md5str1 = new SimpleHash("md5",password,salt).toBase64();
String md5str2 = new SimpleHash("md5",password,salt,5).toBase64();//散列次数
String sha1str = new SimpleHash("sha1",password,salt).toBase64();
String sha2str = new SimpleHash("sha1",password,salt,5).toBase64();//散列次数
String sha256str = new SimpleHash("sha-256",password,salt).toBase64();
String sha256str2 = new SimpleHash("sha-256",password,salt,5).toBase64();//散列次数
System.out.println(md5str1);
System.out.println(md5str2);
System.out.println(sha1str);
System.out.println(sha2str);
System.out.println(sha256str);
System.out.println(sha256str2);
PasswordService
它用于对明密码进行加密,以及判断密码是否匹配。 PasswordService是个接口它实现了DefaultPasswordService。
- 默认
DefaultPasswordService默认使用的加密SHA-256和散列次数500000次,如果不想使用它可以重写PasswordService接口,实现自定义。
public static final String DEFAULT_HASH_ALGORITHM = "SHA-256";
public static final int DEFAULT_HASH_ITERATIONS = 500000; //500,000
String password="pwd123";
PasswordService passwordService=new DefaultPasswordService();
String encryptPassword = passwordService.encryptPassword(password);
System.out.println(encryptPassword);
- 自定义:
PasswordSerivceImpl
public class PasswordSerivceImpl implements PasswordService{
private String algorithmName;//算法名称
private String salt;//盐值
private int hashInterations;//散列次数
/*
* 用于对文明密码进行加密
* */
@Override
public String encryptPassword(Object plaintextPassword) throws IllegalArgumentException {
return new SimpleHash(algorithmName,plaintextPassword,salt,hashInterations).toBase64();
}
/*
* 用于密码进行匹配
* */
@Override
public boolean passwordsMatch(Object submittedPlaintext, String encrypted) {
String encryPassword=encryptPassword(submittedPlaintext);
return encryPassword.equals(encryPassword);
}
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
}
public void setSalt(String salt) {
this.salt = salt;
}
public void setHashInterations(int hashInterations) {
this.hashInterations = hashInterations;
}
}
test
String password="pwd123";
String salt="salt";
PasswordSerivceImpl passwordService=new PasswordSerivceImpl();
passwordService.setAlgorithmName("sha1");
passwordService.setHashInterations(4);
passwordService.setSalt(salt);
String encryptPassword = passwordService.encryptPassword(password); //加密
System.out.println(encryptPassword);
boolean passwordsMatch = passwordService.passwordsMatch(password, encryptPassword);//判断是否匹配
System.out.println(passwordsMatch);
CredentialsMatcher
CredentialsMatcher实现类
SimpleCredentialsMatcher
默认的密码匹配器,直接判断密码是否相同。
HashedCredentialsMatcher
散列密码匹配器。这个类可以对密码加密做比较,但是如果用盐值加密的数据,数据库中如果没有password_salt列的话又无法直接比较。如果数据库中没有password_salt列 ,又想使用盐值则需要重写JdbcRealm
- 不带盐值,普通的加密,如md5
可以查看 CredentialsMatcher 实现类
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
shiro.ini
#配置数据源
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicde=false&characterEncoding=utf-8
dataSource.username=root
dataSource.password=123456
# 使用JdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource=$dataSource
jdbcRealm.authenticationQuery=select password from t_user where login_name=?
#配置密码匹配器 如果不配置默认调用的是SimpleCredentialsMatcher,它只是对两个密码不错任何操作直接判断。
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5 //只采用md5加密
#将密码匹配器注入到JdbcRealm中
jdbcRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$jdbcRealm
main
public static void main(String[] args) {
Subject subject= ShiroUtils.getSubject("classpath:shiro04/shiro.ini");
UsernamePasswordToken token=new UsernamePasswordToken("tom","123");
try {
subject.login(token);
System.out.println("是否认证:"+subject.isAuthenticated());
} catch(UnknownAccountException e){
System.out.println("未知的账户!e:"+e.getMessage());
} catch (IncorrectCredentialsException e){
System.out.println("错误的密码!e:"+e.getMessage());
} catch (AuthenticationException e) {
System.out.println("认证异常!e:"+e.getMessage());
}
}
- 带盐值
main
public static void main(String[] args) {
Subject subject= ShiroUtils.getSubject("classpath:shiro04/shiro.ini");
UsernamePasswordToken token=new UsernamePasswordToken("tom","123");
try {
subject.login(token);
System.out.println("是否认证:"+subject.isAuthenticated());
} catch(UnknownAccountException e){
System.out.println("未知的账户!e:"+e.getMessage());
} catch (IncorrectCredentialsException e){
System.out.println("错误的密码!e:"+e.getMessage());
} catch (AuthenticationException e) {
System.out.println("认证异常!e:"+e.getMessage());
}
}
JdbcsaltRealm
public class JdbcsaltRealm extends JdbcRealm {
public JdbcsaltRealm()
{
setSaltStyle(SaltStyle.COLUMN);
}
}
shiro.ini
#配置数据源
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro?useUnicde=false&characterEncoding=utf-8
dataSource.username=root
dataSource.password=123456
# 使用自定义的JdbcsaltRealm
jdbcRealm=shiro04.JdbcsaltRealm
jdbcRealm.dataSource=$dataSource
# 重写带salt语句 以login_name当盐值
jdbcRealm.authenticationQuery=select password,login_name from t_user where login_name=?
#配置密码匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=3
#将密码匹配器注入到JdbcRealm中
jdbcRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$jdbcRealm