Shiro 简介概述
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
常用功能
- Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
核心组件:Subject (当前操作用户及其操作)、SecurityManager(安全管理器)、Realms(数据源,操作数据源)。
RBAC权限模型
RBAC(Rela Based Access Controller 基于角色访问的),在数据库中就是角色表、行为表、权限表之间的关系绑定,权限绑定角色和行为。
Shiro 基本使用
框架搭建
- 引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.0</version>
</dependency>
- 定义数据源
@Component()
public class CustomRealm extends AuthorizingRealm {
//认证信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//授权信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
- 创建配置文件
@Configuration
public class ShiroConfig {
/**
* 配置数据源,实则可以在服务层将数据传递进来
* @return
*/
@Bean("realm")
public Realm realm(CustomRealm realm){
return realm;
}
/**
* 创建安全管理器,并配置数据源
* @param realm 数据源
* @return
*/
@Bean("securityManager")
public DefaultWebSecurityManager securityManager (@Qualifier("realm") Realm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* 创建请求过滤器
* @param securityManager 安全管理器
* @return
*/
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean (@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//注册过滤器
shiroFilterFactoryBean.setFilters();
//用户认证时path
shiroFilterFactoryBean.setLoginUrl("path");
//用户认证成功后的path
shiroFilterFactoryBean.setSuccessUrl("path");
//设置未授权跳转path
shiroFilterFactoryBean.setUnauthorizedUrl("path");
//注入securityManager
factoryBean.setSecurityManager(securityManager);
return factoryBean;
}
}
认证授权
- 配置数据源
//从Mysql中读取数据,配置数据源
@Component()
public class CustomRealm extends AuthorizingRealm {
//认证信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//创建授权对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//获取主体也就是->用户名
String username = (String) principalCollection.getPrimaryPrincipal();
//根据获取的用户名查询访问权限(访问路径)->从底层数据库中读取
List<Strinrg> privileges = userService.findPrivilegedByUsername(username);
//绑定用户和权限
simpleAuthorizationInfo.addRoles(privileges);
return simpleAuthorizationInfo;
}
//授权信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取传递过来的用户名->也就是登录时的用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername()
//通过得到的用户名查询数据库存储的密码->在此之前可以确认用户名是否存在
String password = userServer.findPassword(username);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, this.getName());
return info;
}
}
- 身份验证
//获取用户账号和密码
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//创建委托人
Subject currentUser = SecurityUtils.getSubject();
//查看处理结果
try {
subject.login(token);
} catch (UnknownAccountException e) {
error = "用户名/密码错误";
} catch (IncorrectCredentialsException e) {
error = "用户名/密码错误";
} catch (AuthenticationException e) {
//其他错误,比如锁定,如果想单独处理请单独catch处理
error = "其他错误:" + e.getMessage();
}
- 授权管理
//使用注解 @RequiresAuthentication 需要完成用户登录 或者 @RequiresPermissions() 需要对应的权限
@RequestMapping("/user")
public class ProjectController {
/**
* 使用@RequiresAuthentication注解,检验是否完成用户登录
* 使用@RequiresPermissions() 需要对应的权限
*/
@RequiresPermissions("/queryAllPrincipal")
@PostMapping("/queryAllPrincipal")
public ResponseResult searchAllLikePrincipal(@RequestBody FrontProject frontProject, @RequestParam(required = false,defaultValue = "1") int pageNum, @RequestParam(required = false,defaultValue = "10") int pageSize){
ResponseResult result = new ResponseResult();
result.setData(this.projectService.queryAllPrincipal(frontProject,pageNum,pageSize));
return result;
}
}