目录
基础知识
shiro 功能
Authentication:认证;
Authorization:授权;
Session Management:会话管理,shiro拥有自己的session管理机制;
Cryptography:加密解密,管理如密码等数据;
Caching:缓存支持;
Concurrency:shiro 并发验证;
Testing:提供测试支持;
Run As:身份授权访问;
Remember Me:记住我;
架构
这部分资料比较多,不再啰嗦。
Subject:主体,即 “用户”;
SecurityManager:shiro安全管理器,管理 Subject、负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证;
Authorizer:授权;
Realm:安全域,可以有 1 个或多个 Realm;;
SessionManager:绘画管理器;
Cryptography:加密解密模块。
依赖引入
完整项目引入包括:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springboot-shiro-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>
</project>
自定义Realm域,完成认证及授权过程
定义登录接口,触发subject.login(token)
这一步主要完成认证数据(用户名、密码)的获取及token的创建,然后提交shiro进行认证。
@RequestMapping("/sublogin")
public String subLogin(String userName,String password) {
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken(userName,password);
try {
subject.login(token);
subject = SecurityUtils.getSubject();
// 设置shiro session (非必须)
Session shiroSession = subject.getSession();
shiroSession.setAttribute("user",subject.getPrincipal());
return "OK:"+userName;
}catch (UnknownAccountException e) {
return "用户不存在";
} catch (IncorrectCredentialsException e){
return "密码不正确";
}catch (AuthenticationException e) {
return "登录失败";
}
}
自定义Realm域,用于完成认证及授权过程。
此步骤主要完成认证、授权两个过程,并对其中认证中的密码匹配器进行了重写(MD5).
认证: 主要用于构建认证对象(从数据库读取),用于提供给shiro进行认证操作。
授权: 主要用于读取(数据库)授权信息,提供给shiro,以完成授权检查。
package cn.com.demo.shiro.config;
import cn.com.demo.shiro.entity.TShiroUser;
import cn.com.demo.shiro.service.UserService;
import cn.com.demo.shiro.util.MD5;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* 自定义域,完成认证及授权过程
*/
@Component
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 拿到当前用户
Subject subject = SecurityUtils.getSubject();
TShiroUser user = (TShiroUser) subject.getPrincipal();
// 设置权限
simpleAuthorizationInfo.setStringPermissions(userService.getUserPermission(user.getId()));
return simpleAuthorizationInfo;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if(StringUtils.isEmpty(token.getUsername())){
// 用户名为空
return null;
}
TShiroUser user = userService.getUserByUserName(token.getUsername());
if(user == null) {
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
/**
* 密码匹配器
* @return
*/
@Override
public CredentialsMatcher getCredentialsMatcher() {
return new CredentialsMatcher() {
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
UsernamePasswordToken utoken = (UsernamePasswordToken) authenticationToken;
//获得用户输入的密码
String formPassword = new String(utoken.getPassword());
//获得数据库中的密码
String dbPassword = (String) authenticationInfo.getCredentials();
try {
formPassword = MD5.encrypt(formPassword);
return dbPassword.equalsIgnoreCase(formPassword);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
};
}
}
shiro 配置类,配置FactoryBean及web安全管理器
FactoryBean: 用于构建shiro环境及配置,配置各类资源权限,鉴权信息(如登录、授权失败等)、安全管理器等配置
web安全管理器: 用于向shiro提供自定义Realm域。
此过程中,完成Factory到Realm的关联。
package cn.com.demo.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.com.demo.shiro.entity.TShiroResPermission;
import cn.com.demo.shiro.service.UserService;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* shiro 配置类,配置FactoryBean及web安全管理器
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired DefaultWebSecurityManager defaultWebSecurityManager, @Autowired UserService userService){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// shiro 内置过滤器 org.apache.shiro.web.filter.mgt.DefaultFilter
Map<String, String> map = new LinkedHashMap<>();
// 登录相关免认证接口
map.put("/auth/**","anon");
map.put("/index","anon");
// 需要demo:p1 权限
// map.put("/demo/p1","perms[demo:p1]");
// 需要demo:p2 权限
// map.put("/demo/p2","perms[demo:p2]");
// 从数据库中读取权限信息
List<TShiroResPermission> tShiroResPermissionList = userService.getResPermission();
for (TShiroResPermission tShiroResPermission : tShiroResPermissionList) {
map.put(tShiroResPermission.getResPath(),"perms["+tShiroResPermission.getPermissionCode()+"]");
}
// 需要登录的接口
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 设置登录页地址,前后端分离项目注意返回需要登录状态码等信息
shiroFilterFactoryBean.setLoginUrl("/auth/login");
// 设置未授权地址
shiroFilterFactoryBean.setUnauthorizedUrl("/auth/forbidden");
return shiroFilterFactoryBean;
}
/**
* 安全管理器
* @param userRealm
* @return
*/
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
}
数据库设计
用户表
CREATE TABLE `t_shiro_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
`password` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
资源权限表
可对应实际生产的角色、菜单、按钮等跟权限有关的表
CREATE TABLE `t_shiro_res_permission` (
`id` int NOT NULL AUTO_INCREMENT,
`res_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '资源(如url)',
`permission_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限代码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
用户权限表
对应用户权限信息;RBAC 权限方案中,还需要用户角色的参与,此表只用于简单表述用户的权限。
CREATE TABLE `t_shiro_user_permission` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL COMMENT '用户id',
`permission_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限代码',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
附
完整源代码