Skip to content

企业系统安全 ​

某公司用户数据被拖库,原因是密码用明文存储、SQL 注入没防住、接口没有鉴权。这不是段子,是每年都在发生的真实事故。身份认证怎么选型?权限怎么设计才能既灵活又安全?敏感数据怎么保护?本章从认证授权、数据安全两个维度,讲清楚企业级 Java 应用的安全底线。

1. 身份认证 ​

1.1 认证的本质 ​

身份认证(Authentication)回答的是"你是谁"的问题。在 Web 应用中,用户首次登录后,后续请求需要某种机制让服务器知道"这个请求来自已认证的用户"。三种主流方案的对比如下:

维度Session-CookieJWT(JSON Web Token)OAuth 2.0
存储位置服务端(内存/Redis)客户端(LocalStorage/Cookie)不存储 token,由授权服务器管理
状态有状态(服务端需保存 Session)无状态(token 自包含信息)依赖授权服务器
跨域需要额外处理(Cookie 跨域限制)天然支持(放在 Header 中)天然支持
扩展性多实例需要 Session 共享任意节点可验证授权中心集中管理
撤销删除 Session 即可困难(需黑名单机制)通过 Refresh Token 机制
适用场景传统单体 Web 应用前后端分离、微服务内部认证第三方登录、开放平台
安全风险CSRF 攻击Token 泄露后难以撤销配置不当可能被滥用

1.2 JWT 的结构 ​

JWT 是目前微服务架构中最常用的身份认证方案,它由三部分组成:

txt
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEwMDg2LCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3MDUzMDAwMDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
│       Header(算法)          │              Payload(声明)                    │            Signature(签名)               │

Java 中生成和验证 JWT:

java
@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration:7200}")
    private long expiration; // 默认 2 小时

    public String generateToken(Long userId, String role) {
        return Jwts.builder()
            .setSubject(String.valueOf(userId))
            .claim("role", role)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
            .signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
            .compact();
    }

    public Claims parseToken(String token) {
        return Jwts.parserBuilder()
            .setSigningKey(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
            .build()
            .parseClaimsJws(token)
            .getBody();
    }

    public boolean isTokenValid(String token) {
        try {
            Claims claims = parseToken(token);
            return !claims.getExpiration().before(new Date());
        } catch (JwtException e) {
            return false;
        }
    }
}

1.3 OAuth 2.0 四种授权模式 ​

OAuth 2.0 是一个授权框架,定义了四种授权模式:

模式流程适用场景
授权码模式用户→授权页→授权码→后端换 Token第三方登录(微信、GitHub)
隐式模式用户→授权页→直接返回 Token(前端)已不推荐,安全隐患大
密码模式用户名+密码直接换 Token自家 App、高度信任的第一方应用
客户端凭证模式客户端 ID+Secret 直接换 Token服务间调用(M2M)

授权码模式的完整流程:

txt
① 用户点击"微信登录"
        │
        ▼
② 浏览器跳转到微信授权页
   https://open.weixin.qq.com/authorize?client_id=xxx&redirect_uri=xxx&scope=userinfo
        │
        ▼
③ 用户确认授权,微信回调 redirect_uri 并携带授权码
   https://myapp.com/callback?code=AUTH_CODE_xxx
        │
        ▼
④ 后端用授权码换取 Access Token(服务器间通信,用户无感知)
   POST https://api.weixin.qq.com/oauth/access_token
   Body: client_id=xxx&client_secret=xxx&code=AUTH_CODE_xxx
        │
        ▼
⑤ 获得 Access Token,调用微信 API 获取用户信息
   GET https://api.weixin.qq.com/sns/userinfo?access_token=xxx&openid=xxx

2. Spring Security 核心 ​

2.1 整体架构 ​

Spring Security 的本质是一条 Servlet Filter Chain(过滤器链),每个请求都要经过这条链的处理:

txt
HTTP Request
    │
    ▼
┌──────────────────────────────────────────────────────────────────┐
│                   DelegatingFilterProxy                          │
│  (Spring 容器的入口,委托给 FilterChainProxy)                       │
│                                                                  │
│  ┌─────────────────────────────────────────────────────────────┐ │
│  │              FilterChainProxy                               │ │
│  │                                                             │ │
│  │  ┌──────────┐  ┌──────────────────┐  ┌──────────────────┐   │ │
│  │  │ Security │  │ Authentication   │  │  Authorization   │   │ │
│  │  │ Context  │  │     Filter       │  │     Filter       │   │ │
│  │  │  Filter  │  │ (认证:你是谁?)   │  │ (授权:你能做什么?)│   │ │
│  │  └──────────┘  └──────────────────┘  └──────────────────┘   │ │
│  └─────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
    │
    ▼
  Controller

2.2 认证流程详解 ​

一次登录认证的完整流程,各组件按顺序协作:

2.3 核心代码示例 ​

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())  // 前后端分离禁用 CSRF
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/login", "/api/auth/register").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

自定义 JWT 认证过滤器:

java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        String header = request.getHeader("Authorization");

        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);

            if (jwtUtil.isTokenValid(token)) {
                Claims claims = jwtUtil.parseToken(token);
                Long userId = Long.parseLong(claims.getSubject());
                String role = claims.get("role", String.class);

                UsernamePasswordAuthenticationToken auth =
                    new UsernamePasswordAuthenticationToken(
                        userId, null,
                        List.of(new SimpleGrantedAuthority("ROLE_" + role))
                    );

                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }

        chain.doFilter(request, response);
    }
}

3. 权限模型(RBAC) ​

3.1 RBAC 基本模型 ​

RBAC(Role-Based Access Control,基于角色的访问控制)是企业应用中最广泛使用的权限模型:

txt
┌────────┐     M:N      ┌────────┐     M:N      ┌────────────┐
│  用户   │ ◀──────────▶ │  角色   │ ◀──────────▶ │  权限/资源  │
│ (User) │              │ (Role) │              │(Permission)│
└────────┘              └────────┘              └────────────┘

示例:
用户"张三" → 角色"订单管理员" → 权限"order:read", "order:create", "order:refund"
用户"李四" → 角色"客服"       → 权限"order:read", "ticket:create"

RBAC 的五表建表语句、与 Spring Security 的集成(UserDetailsService + @PreAuthorize)等完整实现,见 授权模型 §1,本节只保留模型概览。

4. 数据安全 ​

4.1 HTTPS 传输加密 ​

HTTPS 是最基本的安全措施,确保数据在传输过程中不被窃听和篡改。Spring Boot 配置 HTTPS:

yaml
server:
  port: 443
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: ${SSL_KEYSTORE_PASSWORD}
    key-store-type: PKCS12

生产环境推荐:在 Nginx 或负载均衡器上终止 SSL,后端服务之间使用内网 HTTP 通信,减少证书管理的复杂度。

4.2 字段加密、日志脱敏与操作审计 ​

这三项数据安全能力不依赖 Spring Security,属于通用 Java 技术,落地代码统一见 安全实践:

5. 本章小结 ​

本章从两个维度构建了企业级 Java 应用的安全体系:

维度核心能力关键技术
认证授权身份认证 + 权限控制JWT / OAuth 2.0 / Spring Security / RBAC
数据安全传输加密 + 存储加密 + 日志脱敏 + 审计HTTPS / AES / Logback / AOP

系统安全上线了,但你怎么知道它运行得好不好?用户说"接口很慢",你如何定位是数据库慢、缓存穿透还是下游超时?下一章从日志、指标、链路追踪三大支柱出发,构建完整的可观测体系。

Released under the MIT License.