深度解析 Spring Security:身份验证、授权、OAuth2 和 JWT 身份验证的完整指


深度解析 Spring Security

Spring Security 是一个强大的安全框架,提供了全面的身份验证和授权功能。以下是对 Spring Security 各个关键部分的深入解析,包括身份验证、授权、OAuth2 和 JWT 身份验证。

1. 身份验证(Authentication)

身份验证是验证用户身份的过程。Spring Security 提供了多种身份验证方式,如表单登录、HTTP Basic、LDAP 等。

基本配置

  1. 依赖: 在 pom.xml 中添加 Spring Security 依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  2. 安全配置类: 创建一个配置类,继承 WebSecurityConfigurerAdapter,并重写相关方法。

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                .withUser("user").password("{noop}password").roles("USER")
                .and()
                .withUser("admin").password("{noop}admin").roles("ADMIN");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .httpBasic();
        }
    }
    

2. 授权(Authorization)

授权是确定用户在应用程序中可以执行哪些操作的过程。Spring Security 使用权限和角色来管理授权。

基本授权配置

授权主要通过在 HttpSecurity 中配置 authorizeRequests 来实现。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .antMatchers("/user/**").hasRole("USER")
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .and()
        .httpBasic();
}

3. OAuth2

OAuth2 是一种开放的标准,用于授权委托。Spring Security 提供了对 OAuth2 的支持,允许应用程序通过第三方服务进行身份验证。

配置 OAuth2 登录

  1. 依赖: 在 pom.xml 中添加 OAuth2 依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    
  2. 配置文件: 在 application.yml 中配置 OAuth2 客户端:

    spring:
      security:
        oauth2:
          client:
            registration:
              google:
                client-id: YOUR_CLIENT_ID
                client-secret: YOUR_CLIENT_SECRET
                scope: profile, email
                redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            provider:
              google:
                authorization-uri: https://accounts.google.com/o/oauth2/auth
                token-uri: https://oauth2.googleapis.com/token
                user-info-uri: https://openidconnect.googleapis.com/v1/userinfo
    
  3. 安全配置类: 在 SecurityConfig 中配置 OAuth2 登录:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests(authorizeRequests ->
                    authorizeRequests
                        .antMatchers("/", "/login**").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2Login();
        }
    }
    

4. JWT 身份验证

JWT(JSON Web Token)是一种基于 JSON 的令牌,用于在各方之间安全地传输信息。Spring Security 支持 JWT 身份验证,适用于无状态的 RESTful API。

配置 JWT 身份验证

  1. 依赖: 在 pom.xml 中添加 JWT 依赖:

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    
  2. JWT 工具类: 创建一个工具类,用于生成和解析 JWT。

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import java.util.Date;
    
    public class JwtUtil {
        private static final String SECRET_KEY = "your_secret_key";
    
        public static String generateToken(String username) {
            return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 864_000_000)) // 10 days
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
        }
    
        public static String getUsernameFromToken(String token) {
            return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
        }
    }
    
  3. JWT 过滤器: 创建一个过滤器,拦截每个请求并验证 JWT。

    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
        private final UserDetailsService userDetailsService;
    
        public JwtAuthenticationFilter(UserDetailsService userDetailsService) {
            this.userDetailsService = userDetailsService;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws ServletException, IOException {
            String header = request.getHeader("Authorization");
            String username = null;
            String authToken = null;
            if (header != null && header.startsWith("Bearer ")) {
                authToken = header.substring(7);
                try {
                    username = JwtUtil.getUsernameFromToken(authToken);
                } catch (Exception e) {
                    logger.error("Error extracting username from token", e);
                }
            }
    
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                if (JwtUtil.validateToken(authToken, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
            chain.doFilter(request, response);
        }
    }
    
  4. 安全配置类: 在 SecurityConfig 中注册 JWT 过滤器。

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Bean
        public JwtAuthenticationFilter jwtAuthenticationFilter() {
            return new JwtAuthenticationFilter(userDetailsService);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    

结论

通过 Spring Security 的身份验证、授权、OAuth2 和 JWT 支持,开发者可以构建安全、灵活的 Java 应用程序。Spring Security 提供的全面配置选项和集成能力,使得安全性配置变得简单而强大。


原文链接:codingdict.net