文章目录
一、Shiro简介
- Shiro框架
应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject
- Shiro内部结构图
- 常见单词说明
- Shiro中的Shiro.ini说明
(1) main
提供了对根对象securityManager及其依赖对象的配置
#创建对象
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
其构造器必须是public空参构造器,通过反射创建相应的实例。
1.对象名=全限定类名 相当于调用public无参构造器创建对象
2.对象名.属性名=值 相当于调用于setter方法设置常量值
3.对象名.属性名=$对象引用 相当于调用setter方法设置对象引用
(2)users
提供了对用户/密码及其角色的配置,用户名=密码,角色1,角色2
username=password,role1,role2
例如:配置用户名/密码及其角色,格式:“用户名=密码,角色1,角色2”,角色部分可省略。如:
[users]
zhang=123,role1,role2
wang=123
(3)roles
提供了角色及权限之间关系的配置,角色=权限1,权限2 role1 = permission1 , permission2
例如:配置角色及权限之间的关系,格式:“角色=权限1,权限2”;如:
[roles]
role1=user:create,user:update
role2=*
(4)urls
用于web,提供了对web url拦截相关的配置,url=拦截器[参数],拦截器
/index.html = anon
/admin/** = authc, roles[admin],perms["permission1"]
- 源码分析
认证使用的是 SimpleAccountRealm
二、在开始操作之前 不妨先导入log日志
- maven导包
<!--slf-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
- 在resources目录下新键log4j.properties文件
# 全局日志配置
log4j.rootLogger=ERROR, stdout
# MyBatis 日志配置 【这里面写自己的地址】
log4j.logger.com.luo=TRACE
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
三、第一个Shiro程序
- 导入shiro核心包
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
- 在resources目录下创建一个shiro.ini文件
[users]
xiaohu=123
xiaoluo=456
- 我的测试类
public class TestAuthenticator {
public static void main(String[] args) {
//1.创建安全管理器的对象
// SecurityManager securityManager = new DefaultSecurityManager();
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2.给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3.SecurityUtils全局安全工具类
SecurityUtils.setSecurityManager(securityManager);
//4.关键对象 Subject 主体
Subject subject=SecurityUtils.getSubject();
//5. 创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaohu","1223");
try {
subject.login(token);//用户认证
System.out.println("认证状态:" + subject.isAuthenticated());
} catch (UnknownAccountException Un){
Un.printStackTrace();
System.out.println("认证失败:用户名错误!!");
}catch (IncorrectCredentialsException In){
In.printStackTrace();
System.out.println("认证失败:密码错误!!");
}catch (Exception e){
e.printStackTrace();
}
}
}
四、自定义Realm(认证)
- 认证部分
//自定义的Realm实现 将认证/授权的数据的来源转为数据库的实现
public class CustomerRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//在Token获取用户名
String principal = (String) authenticationToken.getPrincipal();
System.out.println(principal);
//根据身份信息,在数据库中使用 jdbc . mybatis查询相关的数据库 这里为了方便使用假的数据
if("xiaoluo".equals(principal)){
// 参数1:返回数据库中正确的用户名 参数2:返回数据库中正确的密码 参数3:提供当前Realm的名字 this.getName
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
- 测试代码
//自定义realm
public class TestCustomerRealmAuthenticator {
public static void main(String[] args) {
//1.创建安全管理器的对象
// SecurityManager securityManager = new DefaultSecurityManager();
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2.给安全管理器设置realm 不使用shiro.ini 使用自定义的Realm
securityManager.setRealm(new CustomerRealm());
//3.SecurityUtils全局安全工具类
SecurityUtils.setSecurityManager(securityManager);
//4.关键对象 Subject 主体
Subject subject=SecurityUtils.getSubject();
//5. 创建令牌luo
UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","123");
try {
subject.login(token);//用户认证
System.out.println("认证状态:" + subject.isAuthenticated());
} catch (UnknownAccountException Un){
Un.printStackTrace();
System.out.println("认证失败:用户名错误!!");
}catch (IncorrectCredentialsException In){
In.printStackTrace();
System.out.println("认证失败:密码错误!!");
}catch (Exception e){
e.printStackTrace();
}
}
}
五、MD5+salt+hash散列在Shiro中的应用
手动获取自己密码的加密后的字符串
// 参数1:密码 参数2:盐值 参数3:hash散列次数
Md5Hash md5Hash1 = new Md5Hash("123", "XO*7ps", 1024);
System.out.println(md5Hash1.toHex());
- 自定义的Realm
//使用自定义realm加入 md5+ salt +hash
public class CustomerMD5Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//在Token获取用户名
String principal = (String) authenticationToken.getPrincipal();
//根据身份信息,在数据库中使用 jdbc . mybatis查询相关的数据库 这里为了方便使用假的数据
if("xiaoluo".equals(principal)){
// 参数1:返回数据库中正确的用户名 参数2:返回数据库中正确的密码经过 md5和随机盐加密之后的字符串 参数3:随机盐(可以用uuid替换) 参数4:提供当前Realm的名字 this.getName
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
principal,"908da6d8e6057542323d9f58de42f9ab", ByteSource.Util.bytes("XO*7ps"),this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
- 测试类
public class TestCustomerMd5RealmAuthenicator {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//注入Realm
CustomerMD5Realm realm = new CustomerMD5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//使用md5加密
hashedCredentialsMatcher.setHashIterations(1024);//hash散列次数
realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(realm);
//将安全管理器注入安全工具
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
//认证
UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","123");
try {
subject.login(token);//用户认证
System.out.println("认证状态:" + subject.isAuthenticated());
System.out.println("登录成功");
} catch (UnknownAccountException Un){
Un.printStackTrace();
System.out.println("认证失败:用户名错误!!");
}catch (IncorrectCredentialsException In){
In.printStackTrace();
System.out.println("认证失败:密码错误!!");
}catch (Exception e){
e.printStackTrace();
}
}
}
六、自定义Realm(授权)
授权流程
我的测试类
public class TestCustomerMd5RealmAuthenicator {
public static void main(String[] args) {
//创建安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//注入Realm
CustomerMD5Realm realm = new CustomerMD5Realm();
//设置realm使用hash凭证匹配器
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//使用md5加密
hashedCredentialsMatcher.setHashIterations(1024);//hash散列次数
realm.setCredentialsMatcher(hashedCredentialsMatcher);
defaultSecurityManager.setRealm(realm);
//将安全管理器注入安全工具
SecurityUtils.setSecurityManager(defaultSecurityManager);
//通过安全工具类获取subject
Subject subject = SecurityUtils.getSubject();
//认证
UsernamePasswordToken token = new UsernamePasswordToken("xiaoluo","123");
try {
subject.login(token);//用户认证
System.out.println("认证状态:" + subject.isAuthenticated());//true
System.out.println("登录成功");
} catch (UnknownAccountException Un){
Un.printStackTrace();
System.out.println("认证失败:用户名错误!!");
}catch (IncorrectCredentialsException In){
In.printStackTrace();
System.out.println("认证失败:密码错误!!");
}catch (Exception e){
e.printStackTrace();
}
System.out.println("==========");
//认证的用户进行授权
if(subject.isAuthenticated()){
//1.基于单角色权限控制
System.out.println(subject.hasRole("admin"));//true
//2.基于多角色权限控制
System.out.println(subject.hasAllRoles(Arrays.asList("admin", "users")));//true
//3.是否具有其中一个角色
boolean[] booleans=subject.hasRoles(Arrays.asList("admin", "users","super"));
for(boolean aBoolean:booleans){
System.out.println(aBoolean);//true//true//true
}
//4.基于权限字符串的访问控制, 资源标识符 : 操作:资源类型
System.out.println("权限:"+subject.isPermitted("users:*:*"));//false
System.out.println("权限:"+subject.isPermitted("users:*:01"));//true
System.out.println("权限:"+subject.isPermitted("users:del:01"));//true
//5. 分别具有哪些权限
boolean[] permitted = subject.isPermitted("users:*:01", "order:*:01");
for (boolean b:permitted
) {
System.out.println(b);//true false
}
//6. 同时具有哪些权限
boolean permittedAll = subject.isPermittedAll("users:*:01", "order:create:01");
System.out.println(permittedAll);//true
}
}
}
我重写的授权方法(doGetAuthorizationInfo)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
System.out.println("身份信息:"+primaryPrincipal);
//根据身份信息 用户名 获取当前用户的角色信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addRole("users");
simpleAuthorizationInfo.addStringPermission("users:*:01");
simpleAuthorizationInfo.addStringPermission("order:create");
return simpleAuthorizationInfo;
}
七、整合ssm+shiro框架
- 先整合ssm框架
ssm整合博客
注:导入了 SLF4J就不需要导入log4j了
- 编写Shiroconfig类
@Configuration
@PropertySource("classpath:application.properties")
@Import({ShiroBeanConfiguration.class,
ShiroAnnotationProcessorConfiguration.class,
ShiroWebConfiguration.class,
ShiroWebFilterConfiguration.class,
ShiroRequestMappingConfig.class})
public class ShiroConfig {
@Bean
public MyRealm realm(){
return new MyRealm();//自定义的realm
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/view/login", "anon"); //anon是指未登录情况下 可以访问的页面
chainDefinition.addPathDefinition("/user/login", "anon");
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
}
- 创建application.properties类
application.properties
#设置登陆页面地址
shiro.loginUrl =
- 在web.xml 里面编写shiro过滤器
<!--shiro过滤器-->
<filter>
<filter-name>shiroFilterFactoryBean</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilterFactoryBean</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 我的shiroRelam
public class MyShrioRealm extends AuthorizingRealm {
@Resource
private TestService testService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//根据用户名查询角色
//根据角色查询权限
SimpleAuthorizationInfo simpleAuthenticationInfo = new SimpleAuthorizationInfo();
return simpleAuthenticationInfo;
}
//认证方法
@Override
protected SimpleAuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken mytoken=(UsernamePasswordToken) authenticationToken;
String username = mytoken.getUsername();
Person name1 = testService.getName(username);
if(name1==null){
//用户名不存在
throw new AccountException("用戶名不存在");
}
return new SimpleAuthenticationInfo(username,name1.getPassword(), getName());
}
}