开发者

SpringSecurity 鉴权与授权的具体使用

目录
  • 前言
  • 一、基础概念详解
    • 1.SecurityFilterChain(安全过滤器链)
    • 2.UserDetailsService(用户详情服务)
    • 3.PasswordEncoder(密码编码器)
    • 4.JwtRequestFilter(自定义 JWT 过滤器)
    • 5.AuthenticationEntryPoint(鉴权失败处理器)
    • 6.AccessDeniedHandler(权限不足处理器)
  • 二、配置流程详解
    • 成功时的流程:
    • 失败时的流程:
  • 三、实战示例详解
    • 示例 1:JWT Token 工具类(完整版)
    • 示例 2:登录接口
    • 示例 3:多种路径保护方式
  • 结语

    前言

    在现代 Web 开发中,前后端分离架构已经成为主流。后端专注于提供 RESTful API,而前端通过 AJAX 请求与后端交互。在这种架构下,如何对用户进行 认证(Authentication)授权(Authorization) 成为了系统设计中的核心问题。

    Spring Security 是 Spring 框架中用于构建安全系统的模块,它不仅提供了强大的安全机制,还支持灵活的自定义配置。本文将围绕 鉴权失败和成功时的行为、需要拦截的路径配置、以及具体的代码实现方式 展开讲解,并结合多个实际例子,javascript帮助你深入理解 Spring Security 在前后端分离项目中的使用。

    本文将从以下三个方面详细展开:

    • 基础概念:包括认证、授权、关键组件及其作用;
    • 配置流程:从登录请求到权限控制的完整流程;
    • 实战示例:JWT 认证、异常处理、多种路径保护等具体实现;

    无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供一套完整的解决方案。

    一、基础概念详解

    Spring Security 的核心在于 认证(Authentication)授权(Authorization)

    1.SecurityFilterChain(安全过滤器链)

    • 作用:定义 HTTP 请求的安全策略。
    • 位置:通常在配置类中通过 @Bean 注解创建。
    • 示例
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(withDefaults())
            .httpBasic(withDefaults());
    
        return http.build();
    }
    

    可以定义多个 SecurityFilterChain 来处理不同路径的权限策略。

    2.UserDetailsService(用户详情服务)

    • 作用:负责加载用户信息(用户名、密码、权限)。
    • 实现方式
      • 内存方式(适合测试):
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
                               .password(passwordEncoder().encode("123456"))
                               .roles("USER")
                               .build();
        return new InMemoryUserDetailsManager(user);
    }
    
    • 数据库方式(推荐生产环境):
    @Service
    public class CustomUserDetailsService implements UserDetailsService {
    
        @Autowired
        private UserRepository userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userRepository.findByUsername(username)
                    .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
            return new org.springframework.security.core.userdetails.User(
                    user.getUsername(),
                    user.getPassword(),
                    getAuthorities(user.getRoles()));
        }
    
        private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
            return roleshttp://www.devze.com.stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
                    .collect(Collectors.toList());
        }
    }
    

    3.PasswordEncoder(密码编码器)

    • 作用:加密存储密码,并验证密码是否匹配。
    • 常用实现
      • BCryptPasswordEncoder(推荐)
      • NoOpPasswordEncoder(不加密,仅用于开发阶段)
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    

    4.JwtRequestFilter(自定义 JWT 过滤器)

    • 作用:拦截每个请求,解析 Token 并设置当前用户认证信息。
    • 实现方式
    @Component
    public class JwtRequestFilter extends OncePerRequestFilter {
    
        @Autowired
        private JwtTokenUtil jwtTokenUtil;
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain filterChain)
                throws ServletException, IOException {
    
            String token = extractToken(request);
    
            if (token != null && jwtTokenUtil.validateToken(token)) {
                String username = jwtTokenUtil.getUsernameFromToken(token);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
    
            filterChain.doFilter(request, response);
        }
    
        private String extractToken(HttpServletRequest request) {
         javascript   String header = request.getHeader("Authorization");
            if (header != null && header.startsWith("Bearer ")) {
                return header.substring(7);
            }
            return null;
        }
    }
    

    5.AuthenticationEntryPoint(鉴权失败处理器)

    • 作用:当用户未认证访问受保护资源时触发该处理器。
    • 实现方式
    @Component
    public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
        @Override
        public void commence(HttpServletRequest request,
                             HttpServletResponse response,
                             AuthenticationException authException) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\": \"Unauthorized\", \"message\": \"认证失败,请重新登录\"}");
        }
    }
    

    6.AccessDeniedHandler(权限不足处理器)

    • 作用:当用户已认证但没有访问权限时触发。
    • 实现方式
    @Component
    public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest request,
                           HttpServletResponse response,
                           AccessDeniedException accessDeniedException) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("{\"error\": \"Forbidden\", \"message\": \"你没有权限访问该资源\"}");
        }
    }
    

    二、配置流程详解

    成功时的流程:

    1. 用户发送登录请求 → /api/auth/login
    2. 后端验证用户名密码,生成 JWT Token 并返回给前端
    3. 前端保存 Token(如 localStorage)
    4. 后续请求携带 Token(放在 Header 中)
    5. 后端通过自定义 JWT 过滤器解析 Token 并设置认证信息
    6. 用户访问受保护资源成功

    失败时的流程:

    1. Token 缺失或格式错误 → 返回 401 Unauthorized
    2. Token 已过期或签名无效 → 返回 401 Unauthorized
    3. 用户没有权限访问某资源 → 返回 403 Forbidden

    三、实战示例详解

    示例 1:JWT Token 工具类(完整版)

    @Component
    public class JwtTokenUtil {
    
        private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; // 5小时
        private String secret = "your-secret-key-here";
    
        public String getUsernameFromToken(String token) {
            return getClaimFromToken(token, Claims::getSubject);
        }
    
        public Date getExpirationDateFromToken(String token) {
            return getClaimFromToken(token, Claims::getExpiration);
        }
    
        public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
            final Claims claims =编程客栈 getAllClaimsFromToken(token);
            return claimsResolver.apply(claims);
        }
    
        private Claims getAllClaimsFromToken(String token) {
            return Jwts.parserBuilder()
                    .setSigningKey(secret)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        }
    
        private Boolean isTokenExpired(String token) {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        }
    
        public String generateToken(UserDetails userDetails) {
            Map<String, Object> claims = new HashMap<>();
            return doGenerateToken(claims, userDetails.getUsername());
        }
    
        private String doGenerateToken(Map<String, Object> claims, String subject) {
            return Jwts.builder()
                    .setClaims(claims)
                    .setSubject(subject)
                    .setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + JWT_TnINnEOKEN_VALIDITY * 1000))
                    .signWith(SignatureAlgorithm.HS512, secret)
                    .compact();
        }
    
        public Boolean validateToken(String token, UserDetails userDetails) {
            final String username = getUsernameFromToken(token);
            return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        }
    }
    

    示例 2:登录接口

    @RestController
    @RequestMapping("/api/auth")
    public class AuthController {
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private JwtTokenUtil jwtTokenUtil;
    
        @Autowired
        private UserDetailsService userDetailsService;
    
        @PostMapping("/login")
        public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) throws Exception {
            try {
                authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
                );
            } catch (AuthenticationException e) {
                throw new Exception("用户名或密码错误");
            }
    
            final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
            final String token = jwtTokenUtil.generateToken(userDetails);
    
            return ResponseEntity.ok().header("Authorization", "Bearer " + token).build();
        }
    }
    

    示例 3:多种路径保护方式

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            );
    
        return http.build();
    }
    

    结语

    本文从 Spring Security 的基础概念出发,详细介绍了认证与授权的核心组件,并结合多个实战示例展示了 JWT 的集成、路径保护、异常处理等常见场景的实现方式。

    到此这篇关于SpringSecurity 鉴权与授权的具体使用的文章就介绍到这了,更多相关SpringSecurity 鉴权与授权内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜