Spring Security详细介绍及使用含完整代码(值得珍藏)

Spring Security详细介绍及使用含完整代码(值得珍藏)

码农世界 2024-06-09 后端 84 次浏览 0个评论

点击下载《Spring Security详细介绍及使用含完整代码(值得珍藏)》

1. 前言

本文将详细阐述Spring Security的原理、使用方法以及步骤,并通过一个完整的示例来展示如何在Spring Boot项目中集成Spring Security。我们将从Spring Security的基本概念开始,逐步深入到配置和使用,确保读者能够充分理解并掌握Spring Security的核心内容。

2. Spring Security概述

Spring Security是一个功能强大且高度可定制的安全框架,专为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。该框架充分利用了Spring IoC、DI(依赖注入)和AOP(面向切面编程)等核心功能,通过一组可配置的Bean,为应用系统提供声明式的安全访问控制,从而减少了为企业系统安全控制编写大量重复代码的工作量。

Spring Security的核心功能包括用户认证(Authentication)和用户授权(Authorization)。用户认证主要用于验证某个用户是否为系统中的合法主体,即用户能否访问该系统,通常要求用户提供用户名和密码,系统通过校验这些信息来完成认证过程。而用户授权则用于验证某个用户是否有权限执行某个操作,不同用户在系统中可能拥有不同的权限,例如有的用户只能读取文件,而有的用户则能读取和修改文件。系统通常会为不同的用户分配不同的角色,每个角色对应一系列的权限。

此外,Spring Security还提供了多个过滤器,这些过滤器能够拦截进入的请求,并在应用程序处理该请求之前进行某些安全处理,从而增强系统的安全性。用户可以根据自己的需求选择适当的过滤器来保护应用程序。

值得一提的是,在Spring Boot出现之前,整合Spring Security可能较为繁琐,但随着Spring Boot的推出,它为Spring Security提供了自动化配置方案,使得用户可以零配置地使用Spring Security,从而简化了其集成过程。

3. Spring Security原理

Spring Security的原理主要基于过滤器链。当一个请求到达Spring应用时,它首先会经过一系列的过滤器,这些过滤器负责身份验证、授权以及其他安全相关的任务。

Spring Security的过滤器链中包含了多种过滤器,每种过滤器都有其特定的功能。例如:

  • WebAsyncManagerIntegrationFilter用于将Security上下文与Spring Web中用于处理异步请求映射的WebAsyncManager进行集成;

  • SecurityContextPersistenceFilter用于在每次请求处理之前将该请求相关的安全上下文信息加载到SecurityContextHolder中,并在请求处理完成后将这些信息存储到仓储中,如Session,从而维护用户的安全信息;

  • HeaderWriterFilter用于将头信息加入响应中;

  • CsrfFilter用于处理跨站请求伪造;

  • LogoutFilter用于处理退出登录;

  • UsernamePasswordAuthenticationFilter用于处理基于表单的登录请求,从表单中获取用户名和密码进行认证;

  • DefaultLoginPageGeneratingFilter用于在没有配置登录页面时生成一个登录表单页面。

  • BasicAuthenticationFilter用于处理httpBasic登录;

  • ExceptionTranslationFilter用于捕获FilterSecurityInterceptor过滤器抛出的异常等。

    在过滤器链的执行过程中,Spring Security会首先检查请求是否是登录请求,并检查登录请求中是否带有用户名和密码。如果有,过滤器会尝试使用这些信息进行登录认证。如果没有,过滤器会将请求回放过给下一个过滤器处理。

    4. Spring Security使用

    在前后端分离的应用中,Spring Security 依然可以用于保护后端 API。前后端分离通常意味着前端(如使用React, Vue, Angular等构建的单页应用)会发送HTTP请求到后端的RESTful API。这种情况下,Spring Security 的配置会有些不同,因为通常不再需要处理传统的会话管理(如JSP页面)。

    下面是一个Spring Security在前后端分离应用中的基本配置示例,使用OAuth2的JWT(JSON Web Tokens)作为认证机制。

    4.1 依赖配置

    首先,添加必要的依赖到你的pom.xml文件中:

      
          
          
            org.springframework.boot  
            spring-boot-starter-security  
          
          
          
          
            io.jsonwebtoken  
            jjwt  
            0.9.1  
          
      
          
    
    

    4.2 Security配置

    然后,创建一个配置类来设置Spring Security:

    import org.springframework.context.annotation.Bean;  
    import org.springframework.context.annotation.Configuration;  
    import org.springframework.security.authentication.AuthenticationManager;  
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;  
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;  
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;  
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;  
    import org.springframework.security.core.userdetails.User;  
    import org.springframework.security.core.userdetails.UserDetailsService;  
    import org.springframework.security.crypto.password.PasswordEncoder;  
    import org.springframework.security.oauth2.jwt.Jwt;  
    import org.springframework.security.oauth2.jwt.JwtDecoder;  
    import org.springframework.security.oauth2.jwt.NioJwtDecoder;  
      
    import java.util.Collections;  
      
    @Configuration  
    @EnableWebSecurity  
    public class SecurityConfig extends WebSecurityConfigurerAdapter {  
      
        // 模拟用户服务,实际开发中应该使用数据库或其他持久化存储  
        @Bean  
        @Override  
        public UserDetailsService userDetailsService() {  
            // 创建内存中的用户,实际开发中应从数据库或其他存储中获取  
            User user = User.withUsername("user")  
                    .password(passwordEncoder().encode("password"))  
                    .roles("USER")  
                    .build();  
      
            return new InMemoryUserDetailsManager(Collections.singletonList(user));  
        }  
      
        @Bean  
        public PasswordEncoder passwordEncoder() {  
            // 使用BCrypt进行密码编码  
            return new BCryptPasswordEncoder();  
        }  
      
        @Bean  
        @Override  
        protected AuthenticationManager authenticationManager() throws Exception {  
            return super.authenticationManager();  
        }  
      
        @Bean  
        public JwtDecoder jwtDecoder() {  
            // 这里使用固定的密钥,实际开发中应该使用安全的密钥管理方式  
            String secret = "mySecret";  
            return JwtDecoders.fromSecretKey(secret);  
        }  
      
        @Override  
        protected void configure(HttpSecurity http) throws Exception {  
            http  
                .csrf().disable()  
                .authorizeRequests()  
                .antMatchers("/login").permitAll() // 允许所有用户访问登录端点  
                .anyRequest().authenticated() // 其他所有请求需要认证  
                .and()  
                .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);  
        }  
    }
    

    4.3 用户服务

    在实际应用中,你可能需要一个用户服务来从数据库中获取用户信息。这里我们创建一个简单的UserService类,但在本示例中不会使用它,因为我们在配置中使用了内存中的用户。

    import org.springframework.security.core.userdetails.User;  
    import org.springframework.security.core.userdetails.UserDetails;  
    import org.springframework.security.core.userdetails.UserDetailsService;  
    import org.springframework.security.core.userdetails.UsernameNotFoundException;  
    import org.springframework.stereotype.Service;  
      
    @Service  
    public class UserService implements UserDetailsService {  
      
        @Override  
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
            // 在实际应用中,这里应该根据用户名从数据库或其他数据源加载用户  
            // 假设我们有一个用户名为"user",密码为"password"的用户  
            if ("user".equals(username)) {  
                return new User("user", "$2a$10$dXJ3SW6G7P50lGmMkkmwe.20c02F55t218l9.kR6l/.6",  
                        new ArrayList<>());  
            } else {  
                throw new UsernameNotFoundException("User not found with username: " + username);  
            }  
        }  
    }
    

    4.4 创建JWT认证过滤器

    这个过滤器会检查HTTP请求头中的Authorization字段,验证JWT令牌,并在验证成功后将用户信息放入Spring Security的上下文中。

    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;  
    import org.springframework.security.core.Authentication;  
    import org.springframework.security.core.context.SecurityContextHolder;  
    import org.springframework.security.core.userdetails.UserDetails;  
    import org.springframework.security.oauth2.jwt.Jwt;  
    import org.springframework.security.oauth2.jwt.JwtDecoder;  
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;  
    import org.springframework.stereotype.Component;  
    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;  
      
    @Component  
    public class JwtAuthenticationFilter extends OncePerRequestFilter {  
      
        private final JwtDecoder jwtDecoder;  
      
        public JwtAuthenticationFilter(JwtDecoder jwtDecoder) {  
            this.jwtDecoder = jwtDecoder;  
        }  
      
        @Override  
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
                throws ServletException, IOException {  
            final String authorizationHeader = request.getHeader("Authorization");  
      
            String username = null;  
            String jwt = null;  
      
            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {  
                jwt = authorizationHeader.substring(7);  
                try {  
                    Jwt jwtParsed = jwtDecoder.decode(jwt);  
                    username = jwtParsed.getSubject();  
                } catch (Exception e) {  
                    // 无效的JWT令牌  
                    logger.warn("Invalid JWT token");  
                }  
            }  
      
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {  
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);  
      
                if (jwt != null) {  
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =  
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
                    usernamePasswordAuthenticationToken  
                            .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));  
      
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);  
                }  
            }  
      
            filterChain.doFilter(request, response);  
        }  
    }
    

    请注意,这个过滤器依赖于JwtDecoder和UserDetailsService。JwtDecoder用于解码JWT令牌,而UserDetailsService用于加载用户详细信息。在实际应用中,您可能需要将这些依赖项通过构造函数注入到过滤器中,就像上面的代码示例所做的那样。

    此外,这个过滤器检查HTTP请求头中的Authorization字段,并期望它以Bearer 的形式出现。如果令牌有效,过滤器会创建一个UsernamePasswordAuthenticationToken并将其设置为Spring Security的上下文中的认证信息。如果令牌无效或不存在,过滤器不会进行任何操作,并允许请求继续通过过滤器链。

    5. 总结

    通过以上详细的讲解和示例,我们深入了解了Spring Security的原理、使用方法以及步骤。Spring Security作为Spring生态中不可或缺的安全框架,提供了全面的安全解决方案,帮助开发者简化安全配置,确保应用的安全性。在实际项目中,我们可以根据具体需求进行配置,以满足不同的安全要求。通过本文的学习,相信读者已经掌握了Spring Security的核心内容,并能够在实际项目中灵活运用。

    点击下载《Spring Security详细介绍及使用含完整代码(值得珍藏)》

转载请注明来自码农世界,本文标题:《Spring Security详细介绍及使用含完整代码(值得珍藏)》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,84人围观)参与讨论

还没有评论,来说两句吧...

Top