什么是Shiro?
是一款主流的java安全框架,不依赖任何容器,可以运行在java SE和Java EE项目中,它的主要作用是对访问系统的用户进行身份认证,授权,会话管理,加密等操作。
- 以上功能其实是可以用过滤器替代的,但是当遇到大型项目是在使用过滤器就会很不方便了,框架的使用更加的方便。
- Shiro就是用来解决安全管理的系统化框架。
Shiro核心组件
- UsernamePasswordToken : 用来封装用户的登陆信息,使用用户的登陆信息来创建令牌Token
- SecurityManager : Shiro的核心部分,负责安全认证和授权。
- Subject : Shiro的一个抽象概念,包含了用户信息
- Realm : 开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在Realm中
- AuthenticationInfo : 用户的角色信息集合,认证时使用
- AuthorzationInfo : 角色的权限信息集合,授权时使用
- DefaultWebSecurityDManager : 安全管理器,开发者自定义的Realm需要注入到DefaultWebSecurityDManager进行管理才能生效
- ShiroFilterFactoryBean : 过滤器工厂,Shiro的基本运行机制是开发者定制规则,Shiro去执行,具体的执行操作就是由ShiroFilterFactoryBean创建一个个的Filter对象去完成。其实就是把DefaultWebSecurityDManager注入到ShiroFilterFactoryBean。
springboot整合Shiro
- 创建工程
- 导入这几个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<!--mybatis插件-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!--mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
查看构建环境
2. 创建一个实体类和接口(用的是mybatis-plus插件)
连接数据库
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/chaoge
#把输出语句打印出来
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 编写一个测试类来测试这个mapper接口
点击运行:结果出现错误
这个错误说明mapper没有被扫描到,需要加:
运行成功
- 编写一个测试类来测试service接口
上述就是根据用户名进行登陆,现在要使用Shiro这个插件进行认证和授权
- 自定义一个Realm(认证和授权的逻辑全写在里面)
- 先进行认证的操作
package com.chao.realm;
import com.chao.entity.Account;
import com.chao.service.AccountService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
public class AccountRealm extends AuthorizingRealm {
@Autowired
AccountService accountService;
/**
* 角色的授权处理
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 用户的的角色处理(认证)
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//把用户传过来的用户信息放到UsernamePasswordToken(里面封装很多方法)
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//把传过来的用户的名字到数据库去查
Account account = accountService.findByUsername(token.getUsername());
if (account != null) {
/*
* 验证该用户名的密码是否正确
* account.getPwd()是根据名字查到的用户的密码它要和token里的密码进行比较
* 如果密码不对,就会抛出密码不正确的异常
* */
return new SimpleAuthenticationInfo(account,account.getPwd(),getName());
}
return null;
}
}
开发者自定义的Realm需要注入到DefaultWebSecurityDManager进行管理才能生效然后在把DefaultWebSecurityDManager注入到ShiroFilterFactoryBean。
- 创建一个config
package com.chao.config;
import com.chao.realm.AccountRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
//把自定义的Realm放到ioc中
@Bean
public AccountRealm accountRealm() {
return new AccountRealm();
}
/*
* ioc容器里的类可以取出来,每一个加入到ioc容器里的类都会有一个名字,这个名字就是它的方法名
* 当取出来的时候用@Qualifier("accountRealm")进行取
* */
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(accountRealm);
return manager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(defaultWebSecurityManager);
return factoryBean;
}
}
编写认证和授权规则
-
认证过滤器:
- anon:无需认证,游客也能进去
- authc:必须认证
- authcBasic:需要通过HTTPBasic认证
- user: 不一定通过认证,只要曾经被Shiro记录即可
-
授权过滤器
- perms:必须拥有某个权限才能访问
- role:必须拥有某个角色才能访问
- port:请求的端口必须是指定的值才能访问
- rest:请求必须基于Restful风格,PUT,POST,DELETE,GET
- ssl:必须是安全的URL请求,协议HTTPS
创建3个页面,main.html,manage.html,administator.html
访问权限如下:
- 必须登陆才能访问main.html
- 当前用户必须拥有manage授权才能访问manage.html
- 当前用户必须拥有administator角色才能访问administator.html
创建controller层
配置视图解析器
在templates中写:
里面分别写上administator , main , manage
访问main页面
结果:
这里并没有写login.jsp但是会有自动跳到这个页面是因为Shiro有默认的login.jsp
造成以上原因是:
在写一个index页面进行比较
当访问index时是可以正常访问的因为这个页面没有设置过滤器。
但是当点击超链接时还是不能正常访问的,因为它被设置了过滤器。
接下来就自定义一个login页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<table>
<tr>
<td>用户名:</td>
<td>
<input type="text" name="username">
</td>
</tr>
<tr>
<td>密码:</td>
<td>
<input type="password" name="pwd">
</td>
</tr>
<tr>
<td>
<input type="submit" value="登陆">
</td>
</tr>
</table>
</form>
</body>
</html>
自定义登陆页面配置到Shiro中
演示如下:
能正常访问了。
接下来就是写controller里的login
在login.html页面中加入:
那么就可以使用thymeleaf了:
这里用的数据库信息:
根据图可以看出zhangsan只能访问main其它的页面都不能访问,lisi可以访问main , manage 但是不能访问administator而ww全部都能访问。
如图:当lisi登进去后
可以访问以下两个页面
但是不能访问administator页面
返回错误页面的信息用户根本看不懂,为了增加用户体验,可以写一个错误页面
结果:
写欢迎登陆用户和退出的代码
Shiro整合thymeleaf(可以很方便的展示出登陆者拥有的权限和角色并把拥有的页面显示出来,不拥有的则不会显示)
导入依赖:
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
配置添加ShiroDialect。
加入地址:
index.html改写为:
结果:
它拥有main和manage