认证授权
介绍
认证(Authentication)和授权(Authorization)是后端系统安全的基石。认证是验证用户身份的过程,确保用户是其声称的身份;授权是确定已认证用户对系统资源的访问权限的过程。有效的认证授权机制可以防止未授权访问,保护系统和数据的安全。
原理
认证原理
- 知识因素:用户知道的信息,如密码、PIN码
- 持有因素:用户拥有的物品,如手机、令牌
- 生物因素:用户的生物特征,如指纹、面部识别
- 多因素认证(MFA):结合多种认证因素,提高安全性
- 会话管理:认证成功后维护用户会话状态
- 单点登录(SSO):一次认证,多处使用
- 联合认证:使用第三方身份提供商进行认证
授权原理
- 自主访问控制(DAC):资源所有者控制访问权限
- 强制访问控制(MAC):系统根据安全策略强制控制访问
- 基于角色的访问控制(RBAC):根据用户角色分配权限
- 基于属性的访问控制(ABAC):根据用户和资源属性动态决定权限
- 最小权限原则:只授予用户完成工作所需的最小权限
- 权限继承:子资源继承父资源的权限
- 权限回收:定期或按需回收不再需要的权限
常见认证协议
- OAuth 2.0:授权框架,允许第三方应用获取有限访问权限
- OpenID Connect(OIDC):基于OAuth 2.0的身份认证协议
- JWT(JSON Web Token):紧凑的URL安全令牌,用于表示声明
- SAML(Security Assertion Markup Language):基于XML的身份验证协议
- Kerberos:网络认证协议,使用票据验证身份
图示
认证授权流程
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 用户 │ │ 认证服务器 │ │ 资源服务器 │
└─────────┬─────┘ └─────────┬─────┘ └─────────┬─────┘
│ │ │
1. 请求认证 │ │ │
───────────► │ │
│ │ │
2. 提供凭证 │ │ │
◄────────── │ │
│ │ │
3. 验证凭证 │ │ │
───────────► │ │
│ │ │
4. 颁发令牌 │ │ │
◄────────── │ │
│ │ │
5. 请求资源 │ │ │
└───────────────────────► │
│ │
6. 验证令牌 │ │ │
└───────────────────────►
│ │
7. 授权决策 │ │ │
◄───────────────────────┘
│ │
8. 响应请求 │ │ │
◄───────────────────────┘ │
│
│ │ │
9. 返回资源 │ │ │
◄───────────────────────────────────────────────────────────┘
JWT结构
┌─────────────────────────────────────────────────────────────────┐
│ 头部 (Header) │
│ {"alg": "HS256", "typ": "JWT"} │
├─────────────────────────────────────────────────────────────────┤
│ 载荷 (Payload) │
│ {"sub": "1234567890", "name": "John Doe", "iat": 1516239022} │
├─────────────────────────────────────────────────────────────────┤
│ 签名 (Signature) │
│ HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) │
└─────────────────────────────────────────────────────────────────┘
RBAC模型
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 用户 │ │ 角色 │ │ 权限 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
│ 多对多关系 │ 多对多关系 │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ 用户-角色-权限映射表 │
└─────────────────────────────────────────────────────────────────┘
实例
JWT认证示例 (Node.js)
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
app.use(express.json());
// 密钥,实际应用中应存储在环境变量中
const SECRET_KEY = 'your-secret-key';
// 用户数据,实际应用中应存储在数据库中
const users = [
{ id: 1, username: 'admin', password: 'admin123', role: 'admin' },
{ id: 2, username: 'user', password: 'user123', role: 'user' }
];
// 登录接口
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: '认证失败' });
}
// 生成JWT令牌
const token = jwt.sign(
{ userId: user.id, username: user.username, role: user.role },
SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ token });
});
// 认证中间件
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: '未提供认证令牌' });
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ message: '无效的认证令牌' });
}
};
// 授权中间件 - 检查是否为管理员
const authorizeAdmin = (req, res, next) => {
if (req.user && req.user.role === 'admin') {
next();
} else {
res.status(403).json({ message: '没有权限访问此资源' });
}
};
// 受保护的路由 - 所有认证用户可访问
app.get('/api/protected', authenticate, (req, res) => {
res.json({ message: `欢迎 ${req.user.username},这是受保护的资源` });
});
// 管理员路由 - 仅管理员可访问
app.get('/api/admin', authenticate, authorizeAdmin, (req, res) => {
res.json({ message: `欢迎管理员 ${req.user.username},这是管理员资源` });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
OAuth 2.0示例 (Spring Boot)
// 配置类
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginPage("/login")
.defaultSuccessUrl("/home")
.and()
.logout()
.logoutSuccessUrl("/");
}
}
// 控制器
@Controller
public class HomeController {
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/home")
public String home(Principal principal, Model model) {
model.addAttribute("name", principal.getName());
return "home";
}
}
RBAC实现示例 (数据库设计)
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
-- 角色表
CREATE TABLE roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT
);
-- 权限表
CREATE TABLE permissions (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
resource VARCHAR(100) NOT NULL,
action VARCHAR(50) NOT NULL
);
-- 用户-角色关联表
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id),
FOREIGN KEY (permission_id) REFERENCES permissions(id)
);
-- 插入示例数据
INSERT INTO roles (name, description) VALUES
('admin', '管理员角色,拥有所有权限'),
('user', '普通用户角色,拥有基本权限');
INSERT INTO permissions (name, description, resource, action) VALUES
('read_user', '读取用户信息', 'user', 'read'),
('create_user', '创建用户', 'user', 'create'),
('update_user', '更新用户', 'user', 'update'),
('delete_user', '删除用户', 'user', 'delete');
INSERT INTO role_permissions (role_id, permission_id) VALUES
(1, 1), (1, 2), (1, 3), (1, 4), -- 管理员拥有所有权限
(2, 1); -- 普通用户只有读取权限
专业解决方案
认证策略
- 密码策略:复杂性要求、定期更换、密码加密存储
- 多因素认证:结合密码和其他认证因素,如短信验证码、认证器应用
- 单点登录(SSO):实现一次登录,多系统访问
- 无密码认证:使用生物识别、FIDO等技术替代密码
- 会话管理:设置合理的会话超时时间,安全存储会话标识
- 防暴力破解:登录失败次数限制、IP锁定
- 凭证保护:加密存储密码(使用bcrypt、Argon2等算法),不在客户端存储敏感凭证
授权机制
- RBAC实现:设计合理的角色体系,避免角色爆炸
- ABAC实现:基于属性的动态授权,更灵活的访问控制
- 权限细化:最小权限原则,只授予必要的权限
- 权限审计:定期审查用户权限,回收不再需要的权限
- 层次化权限:设计权限的层次结构,便于管理
- 资源级权限:对具体资源进行权限控制
- 条件性权限:基于时间、地点等条件的权限控制
JWT最佳实践
- 密钥管理:安全存储密钥,定期轮换
- 令牌有效期:设置合理的过期时间,避免长期有效
- 令牌包含信息:只包含必要信息,避免敏感信息
- 签名算法:使用强签名算法,如RS256而非HS256
- 令牌刷新:实现令牌刷新机制,避免频繁登录
- 令牌撤销:实现令牌撤销机制,应对令牌泄露
- 传输安全:通过HTTPS传输令牌,避免明文传输
OAuth 2.0最佳实践
- 授权类型选择:根据应用类型选择合适的授权流程
- 客户端密钥保护:安全存储客户端密钥,避免硬编码
- 重定向URI验证:严格验证重定向URI,防止开放重定向攻击
- scope控制:最小权限scope,只请求必要的权限
- PKCE:对于公共客户端,使用PKCE增强安全性
- 令牌验证:资源服务器必须验证令牌的有效性
- 状态参数:使用state参数防止CSRF攻击
安全会话管理
- 会话标识生成:使用强随机数生成会话ID
- 会话存储:安全存储会话数据,避免客户端存储敏感信息
- 会话超时:设置合理的会话超时时间
- 会话绑定:将会话与IP、用户代理等信息绑定
- 会话轮换:登录成功后轮换会话ID
- 登出处理:确保登出时彻底清除会话信息
- 分布式会话:在分布式系统中使用Redis等实现会话共享
工具推荐
- 认证库:Passport.js、Spring Security、Auth0、Okta
- JWT库:jsonwebtoken(Node.js)、jjwt(Java)、PyJWT(Python)
- OAuth 2.0服务:Keycloak、IdentityServer、Auth0、Okta
- 权限管理:Casbin、Shiro、Spring Security ACL
- 密码加密:bcrypt、Argon2、scrypt
- SSO解决方案:Active Directory、OAuth 2.0 + OIDC、SAML
- 多因素认证:Google Authenticator、Authy、Duo Security