Shiro是apache旗下的一个开源安全框架,它可以帮助我们完成身 份认证,授权、加密、会话管理等功能。它有如下特点:
易于理解的API
简单的身份认证,支持多种数据源
简单的授权和鉴权
简单的加密API
支持缓存,以提升应用程序的性能
内置会话管理,适用于Web以及非Web的环境
不跟任何的框架或者容器捆绑,可以独立运行
认证
认证即系统判断用户的身份是否合法,合法可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录、二维码登录、手机短信登录、脸部识别认证、指纹认证等方式。认证是为了保护系统的隐私数据与资源,用户的身份合法才能访问该系统的资源。
授权
授权即认证通过后,根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。 比如在一些视频网站中,普通用户登录后只有观看免费视频的权限,而VIP用户登录后,网站会给该用户提供观看VIP视频的权限。认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,控制不同的用户能够访问不同的资源。举个例子:认证是公司大门识别你作为员工能进入公司,而授权则是由于你作为公司会计可以进入财务室,查看账目,处理财务数据。
Shiro介绍_Shiro核心功能
Shiro介绍_Shiro核心组件
主体。 Subject 在Shiro中是一个接口,接口中定义了认证授权的相关方法。程序通过调用 Subject 的方法进行认证授权,而 Subject 使用SecurityManager 进行认证授权。
权限管理器,它是Shiro的核心。通过 SecurityManager 可以完成具体的认证、授权等操作, SecurityManager 是通过 Authenticator 进行认证,通过Authorizer 进行授权,通过 SessionManager 进行会话管理。 SecurityManager 是一个接口,继承了 Authenticator , Authorizer , SessionManager 三个接口。
认证器。对用户登录时进行身份认证
授权器。用户认证通过后,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
会话管理。shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上。
领域。他是连接数据源+认证功能+授权功能的具体实现。SecurityManager 通过 Realm 获取用户的身份和权限信息,并对用户进行认证和授权。
会话dao,是对会话进行操作的一套接口。它可以将session数据存储到数据库或缓存服务器中。
缓存管理,将用户权限数据存储在缓存中,这样可以减少权限查询次数,提高性能。
密码管理,Shiro提供了一套加密/解密的组件,方便开发。
创建springboot项目
依赖:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.13
com.ss.demo
myshiro
0.0.1-SNAPSHOT
myshiro
myshiro
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
com.baomidou
mybatis-plus-boot-starter
3.5.0
org.apache.shiro
shiro-spring
1.9.0
org.springframework.boot
spring-boot-starter-jdbc
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
org.apache.shiro
shiro-ehcache
1.9.0
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
编写配置文件:
server:
port: 80
# 数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/myshiro?useSSL=false
username: root
password: 123.com
创建包com.ss.demo.controller并创建类PageController
package com.ss.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class PageController {
@RequestMapping("/{page}")
public String showpage(@PathVariable String page){
return page;
}
// 忽略浏览器获取项目图标
@GetMapping("favicon.ico")
@ResponseBody
public void noFavicon(){}
}
拷贝页面资源:
然后启动测试即可,在浏览器输入http://localhost/login
Shiro入门_配置文件认证
Shrio支持多种数据源,我们首先将用户名密码写入配置文件,让 Shiro读取配置文件进行认证。
在 resources 目录创建配置文件 shiro.ini
#声明用户账号
[users]
admin=123
编写登录控制器方法
在包com.ss.demo.controller创建LoginController,进行认证操作【就是登陆操作】
package com.ss.demo.controller;
//登录的认证操作
import com.ss.demo.service.UsersService;
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.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.sql.DataSource;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(String username ,String password){
//1.获得SecurityManager工厂,读取配置文件
IniSecurityManagerFactory factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//2.获得SecurityManager对象
SecurityManager securityManager= factory.getInstance();
//3.将SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//4,获取Subject对象
Subject subject= SecurityUtils.getSubject();
//5.将前端传过来的用户名和密码封装为Shiro提供的身份对象
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
try {
//shiro认证
subject.login(token);
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
return "fail";
}
}
@Autowired
private DataSource dataSource;
/**
* 数据库登录操作
* @param username
* @param password
* @return
*/
@RequestMapping("/user/login2")
public String login2(String username, String password) {
//获取SecurityManager对象
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 2.为SecurityManager对象设置Realm
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
securityManager.setRealm(jdbcRealm);
// 3.将SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 5.将前端传来的用户名密码封装为Shiro提供的身份对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println(token);
try {
//认证操作
subject.login(token);
//跳转到主页面
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
return "fail";
}
}
}
启动项目,访问登录页http://localhost/login,测试登录功能。
输入用户名和密码:
跳转到:
如果输入错误就会跳转到失败页面
之前我们使用配置文件做数据源,在真实开发中,我们往往会使用 数据库作为数据源进行认证操作。 Realm 负责连接数据源并进行具体 认证,它有一个子类 JdbcRealm ,该类可以自动连接数据库认证。
创建数据表 users
CREATE TABLE `users` (
`username` VARCHAR(255) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL
) ENGINE = INNODB CHARACTER SET = utf8;
INSERT INTO `users` VALUES ('admin','123');
在包com.ss.demo.controller创建LoginController 编写登录控制器方法【数据库】
@Autowired
private DataSource dataSource;
/**
* 数据库登录操作
* @param username
* @param password
* @return
*/
@RequestMapping("/user/login2")
public String login2(String username, String password) {
//获取SecurityManager对象
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 2.为SecurityManager对象设置Realm
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
securityManager.setRealm(jdbcRealm);
// 3.将SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 4.获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 5.将前端传来的用户名密码封装为Shiro提供的身份对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println(token);
try {
//认证操作
subject.login(token);
//跳转到主页面
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
return "fail";
}
}
启动项目,访问登录页http://localhost/login,测试登录功能。
输入用户名和密码即可跳转
Shiro认证_将Shiro对象交给容器管理
之前的案例中,所有关于Shiro的对象都是自己创建的。我们在 SpringBoot中使用Shiro,就可以将Shiro的对象交给容器管理,简化业务代码。
创建Shiro配置类
在项目myshiro中创建包com.ss.demo.config,并创建配置类ShiroConfig
package com.ss.demo.config;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ShiroConfig {
//获取SecurityManager对象
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(JdbcRealm jdbcRealm) {
DefaultWebSecurityManager defaultSecurityManager=new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
return defaultSecurityManager;
}
@Bean
public JdbcRealm jdbcRealm(DataSource dataSource) {
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
return jdbcRealm;
}
}
在项目myshiro中创建包com.ss.demo.service,并创建类UsersService;
package com.ss.demo.service;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 用户service
*/
@Service
public class UsersService {
@Autowired
private DefaultWebSecurityManager securityManager;
public void userLogin(String username, String password) {
// 1.将SecurityManager对象设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
// 2.获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 3.将前端传来的用户名密码封装为Shiro提供的身份对象
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
// 4.Shiro认证
subject.login(token);
}
}
在包com.ss.demo.controller创建LoginController 编写登录控制器方法
@Autowired
private UsersService usersService;
/**
* 数据库登录操作
* @param username
* @param password
* @return
*/
@RequestMapping("/user/login3")
public String login3(String username, String password) {
try {
usersService.userLogin(username,password);
return "main";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
}
Shiro认证_自定义Realm
使用 JdbcRealm 认证时,数据库表名、字段名、认证逻辑都不能改变, 我们可以自定义Realm进行更灵活的认证。
修改数据库users:
CREATE TABLE `users` (
`uid` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE = INNODB CHARACTER SET = utf8;
INSERT INTO `users` VALUES (1, 'admin', '123');
编写实体类
在项目中创建包com.ss.demo.entity,并创建实体类Users
package com.ss.demo.entity;
import lombok.Data;
@Data
public class Users {
private Integer uid;
private String username;
private String password;
}
编写mapper接口在包com.ss.demo.mapper中新建UsersMapper
package com.ss.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ss.demo.domain.Users;
/**
* Mapper接口
*/
public interface UsersMapper extends BaseMapper {
}
主启动类扫描Mapper
package com.ss.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.ss.demo.mapper")
@SpringBootApplication
public class MyshiroApplication {
public static void main(String[] args) {
SpringApplication.run(MyshiroApplication.class, args);
}
}
自定义realm
新建包com.ss.demo.realm并新建类MyRealm
package com.ss.demo.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ss.demo.mapper.UsersMapper;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
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;
/**
* 自定义realm
*/
public class MyRealm extends AuthorizingRealm {
//自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//自定义认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
进行认证操作:
package com.ss.demo.realm;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ss.demo.domain.Users;
import com.ss.demo.mapper.UsersMapper;
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;
/**
* 自定义realm
*/
public class MyRealm extends AuthorizingRealm {
@Autowired
private UsersMapper usersMapper;
//自定义授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//自定义认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户输入的用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
//根据用户查询用户信息
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
Users users = usersMapper.selectOne(wrapper);
//将查询到的管理员封装为认证信息
if(users == null) {
throw new UnknownAccountException("用户不存在");
}
//参数1查询的用户信息
//参数2查询的用户密码
//参数3Realm名称
return new SimpleAuthenticationInfo(users, users.getPassword(),"myRealm");
}
}
然后我们回到包com.pb.ss.config的配置类中ShiroConfig把JdbcRealm注释掉,使用我们自定义的MyRealm
package com.ss.demo.config;
import com.ss.demo.realm.MyRealm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ShiroConfig {
//获取SecurityManager对象
/* @Bean
public DefaultWebSecurityManager defaultWebSecurityManager(JdbcRealm jdbcRealm) {
DefaultWebSecurityManager defaultSecurityManager=new DefaultWebSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
return defaultSecurityManager;
}*/
/* @Bean
public JdbcRealm jdbcRealm(DataSource dataSource) {
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
return jdbcRealm;
}*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(MyRealm myRealm){
DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
@Bean
public MyRealm myRealm(){
MyRealm myRealm=new MyRealm();
return myRealm;
}
}
启动测试