最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中接口安全的5種訪問控制方法詳解

 更新時間:2025年05月28日 08:15:14   作者:風(fēng)象南  
在項目開發(fā)種,API接口安全已成為系統(tǒng)設(shè)計中不可忽視的關(guān)鍵環(huán)節(jié),本文將介紹SpringBoot應(yīng)用中實現(xiàn)接口安全的5種訪問控制方法,有需要的小伙伴可以了解下

在項目開發(fā)種,API接口安全已成為系統(tǒng)設(shè)計中不可忽視的關(guān)鍵環(huán)節(jié)。

一個不安全的API可能導(dǎo)致數(shù)據(jù)泄露、未授權(quán)訪問、身份冒用等嚴重安全事件。

本文將介紹SpringBoot應(yīng)用中實現(xiàn)接口安全的5種訪問控制方法。

方法一:基于Spring Security的認證與授權(quán)

原理

Spring Security是Spring生態(tài)系統(tǒng)中負責(zé)安全控制的核心框架,它通過一系列過濾器鏈實現(xiàn)認證和授權(quán)功能。認證(Authentication)確認用戶身份的真實性,而授權(quán)(Authorization)則控制已認證用戶可以訪問的資源范圍。

實現(xiàn)方式

1. 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 配置安全規(guī)則

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/user/**").hasRole("USER")
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginProcessingUrl("/api/login")
                .successHandler(new SimpleUrlAuthenticationSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
            .and()
            .logout()
                .logoutUrl("/api/logout")
                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
            .and()
            .exceptionHandling()
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
    }
}

3. 實現(xiàn)UserDetailsService

@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("User not found: " + username));
        
        return new org.springframework.security.core.userdetails.User(
            user.getUsername(),
            user.getPassword(),
            user.isEnabled(),
            true, true, true,
            getAuthorities(user.getRoles())
        );
    }
    
    private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
        return roles.stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
            .collect(Collectors.toList());
    }
}

4. 創(chuàng)建安全控制器

@RestController
@RequestMapping("/api")
public class SecuredController {

    @GetMapping("/public/data")
    public ResponseEntity<String> publicData() {
        return ResponseEntity.ok("This is public data");
    }
    
    @GetMapping("/user/data")
    public ResponseEntity<String> userData() {
        return ResponseEntity.ok("This is user data");
    }
    
    @GetMapping("/admin/data")
    public ResponseEntity<String> adminData() {
        return ResponseEntity.ok("This is admin data");
    }
    
    @GetMapping("/current-user")
    public ResponseEntity<UserInfo> getCurrentUser(Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return ResponseEntity.ok(new UserInfo(userDetails.getUsername(), userDetails.getAuthorities()));
    }
}

優(yōu)缺點分析

優(yōu)點:

• 功能全面,提供完整的認證和授權(quán)體系

• 與Spring生態(tài)系統(tǒng)無縫集成

• 高度可定制,支持多種認證方式

• 內(nèi)置防護機制,如防止CSRF攻擊、會話固定等

缺點:

• 配置相對復(fù)雜,學(xué)習(xí)曲線較陡

• 默認基于session,在分布式系統(tǒng)中需要額外配置

• 過濾器鏈執(zhí)行可能影響性能

適用場景

• 傳統(tǒng)Web應(yīng)用,特別是有復(fù)雜權(quán)限模型的系統(tǒng)

• 需要細粒度權(quán)限控制的企業(yè)應(yīng)用

• 對安全性要求較高的內(nèi)部系統(tǒng)

方法二:基于JWT的無狀態(tài)認證

原理

JWT(JSON Web Token)是一種緊湊的、自包含的方式,用于在網(wǎng)絡(luò)應(yīng)用環(huán)境間安全地傳輸信息。由于JWT包含了認證所需的所有信息,服務(wù)器無需保存會話狀態(tài),實現(xiàn)了真正的無狀態(tài)認證。

實現(xiàn)方式

1. 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

2. JWT工具類

@Component
public class JwtTokenProvider {

    @Value("${app.jwt.secret}")
    private String jwtSecret;
    
    @Value("${app.jwt.expiration}")
    private long jwtExpiration;
    
    private Key key;
    
    @PostConstruct
    public void init() {
        byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
        this.key = Keys.hmacShaKeyFor(keyBytes);
    }
    
    public String generateToken(Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpiration);
        
        return Jwts.builder()
                .setSubject(userDetails.getUsername())
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .claim("authorities", getAuthorities(userDetails))
                .signWith(key)
                .compact();
    }
    
    public String getUsernameFromToken(String token) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
        
        return claims.getSubject();
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }
    
    private List<String> getAuthorities(UserDetails userDetails) {
        return userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
    }
}

3. JWT認證過濾器

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        String jwt = getJwtFromRequest(request);
        
        if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
            String username = tokenProvider.getUsernameFromToken(jwt);
            
            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 getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

4. 配置JWT安全

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .cors().and()
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .antMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated();
        
        // 添加JWT過濾器
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

5. 認證控制器

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @PostMapping("/login")
    public ResponseEntity<JwtResponse> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                loginRequest.getUsername(),
                loginRequest.getPassword()
            )
        );
        
        SecurityContextHolder.getContext().setAuthentication(authentication);
        
        String jwt = tokenProvider.generateToken(authentication);
        return ResponseEntity.ok(new JwtResponse(jwt));
    }
}

優(yōu)缺點分析

優(yōu)點:

• 無狀態(tài),適合分布式系統(tǒng)和微服務(wù)架構(gòu)

• 減輕服務(wù)器負擔,無需存儲會話

• 跨域支持良好,適合前后端分離應(yīng)用

• 可包含豐富的用戶信息,減少數(shù)據(jù)庫查詢

缺點:

• Token一旦簽發(fā),在過期前無法撤銷(可通過黑名單機制緩解)

• 需要妥善保護密鑰

• Token體積可能較大,增加網(wǎng)絡(luò)傳輸開銷

• 敏感信息不應(yīng)存儲在Token中(雖然簽名,但非加密)

適用場景

• 前后端分離的單頁應(yīng)用(SPA)

• 微服務(wù)架構(gòu)

• 移動應(yīng)用后端

• 跨域API調(diào)用

方法三:OAuth 2.0第三方授權(quán)

原理

OAuth 2.0是一個授權(quán)框架,允許第三方應(yīng)用在不獲取用戶憑證的情況下,獲得對用戶資源的有限訪問權(quán)限。它通過將認證委托給可信的認證服務(wù)器,實現(xiàn)了應(yīng)用間的安全授權(quán)。

實現(xiàn)方式

1. 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.6.8</version>
</dependency>

2. 配置OAuth2資源服務(wù)器

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/user/**").hasRole("USER")
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated();
    }
}

3. 配置OAuth2授權(quán)服務(wù)器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Value("${oauth2.client-id}")
    private String clientId;
    
    @Value("${oauth2.client-secret}")
    private String clientSecret;
    
    @Value("${oauth2.jwt.signing-key}")
    private String signingKey;
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(clientId)
                .secret(passwordEncoder.encode(clientSecret))
                .scopes("read", "write")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(86400);
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter());
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
    
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }
}

4. 安全配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

5. 資源訪問控制器

@RestController
@RequestMapping("/api")
public class ResourceController {

    @GetMapping("/public/data")
    public ResponseEntity<String> publicData() {
        return ResponseEntity.ok("This is public data");
    }
    
    @GetMapping("/user/data")
    public ResponseEntity<String> userData(Principal principal) {
        return ResponseEntity.ok("User data for: " + principal.getName());
    }
    
    @GetMapping("/admin/data")
    public ResponseEntity<String> adminData() {
        return ResponseEntity.ok("This is admin data");
    }
    
    @GetMapping("/me")
    public ResponseEntity<UserInfo> getUserInfo(OAuth2Authentication authentication) {
        return ResponseEntity.ok(new UserInfo(authentication.getName(), authentication.getAuthorities()));
    }
}

6. 配置第三方登錄

@Configuration
@EnableOAuth2Client
public class OAuth2ClientConfig {

    @Bean
    public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
                                                OAuth2ProtectedResourceDetails details) {
        return new OAuth2RestTemplate(details, oauth2ClientContext);
    }
    
    @Bean
    @ConfigurationProperties("github.client")
    public AuthorizationCodeResourceDetails github() {
        return new AuthorizationCodeResourceDetails();
    }
    
    @Bean
    @ConfigurationProperties("github.resource")
    public ResourceServerProperties githubResource() {
        return new ResourceServerProperties();
    }
}

在application.yml中配置:

github:
  client:
    clientId: your-github-client-id
    clientSecret: your-github-client-secret
    accessTokenUri: https://github.com/login/oauth/access_token
    userAuthorizationUri: https://github.com/login/oauth/authorize
    clientAuthenticationScheme: form
  resource:
    userInfoUri: https://api.github.com/user

優(yōu)缺點分析

優(yōu)點:

• 標準化的授權(quán)協(xié)議,廣泛支持

• 支持多種授權(quán)模式(授權(quán)碼、密碼、客戶端憑證等)

• 可實現(xiàn)第三方應(yīng)用登錄(如Google、Facebook、GitHub等)

• 細粒度的權(quán)限范圍控制

缺點:

• 配置較為復(fù)雜

• 理解和實現(xiàn)完整流程有一定門檻

• 在某些場景下可能過于重量級

適用場景

• 需要支持第三方登錄的應(yīng)用

• 企業(yè)內(nèi)多系統(tǒng)間的授權(quán)

• 開放平臺API授權(quán)

• 移動應(yīng)用與服務(wù)端交互

方法四:接口簽名驗證

原理

接口簽名驗證通過對請求參數(shù)、時間戳等信息進行加密簽名,服務(wù)端驗證簽名的有效性來確保請求的合法性和完整性。這種方式常用于開放API的安全防護。

實現(xiàn)方式

1. 簽名生成工具類

@Component
public class SignatureUtils {

    /**
     * 生成簽名
     * @param params 參數(shù)Map
     * @param secretKey 密鑰
     * @return 簽名字符串
     */
    public static String generateSignature(Map<String, String> params, String secretKey) {
        // 1. 參數(shù)按字典序排序
        TreeMap<String, String> sortedParams = new TreeMap<>(params);
        
        // 2. 構(gòu)建簽名字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
            if (!"sign".equals(entry.getKey()) && StringUtils.hasText(entry.getValue())) {
                stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        
        // 3. 添加密鑰
        stringBuilder.append("key=").append(secretKey);
        
        // 4. MD5加密并轉(zhuǎn)大寫
        String signStr = stringBuilder.toString();
        return DigestUtils.md5DigestAsHex(signStr.getBytes()).toUpperCase();
    }
    
    /**
     * 驗證簽名
     * @param params 包含簽名的參數(shù)Map
     * @param secretKey 密鑰
     * @return 是否驗證通過
     */
    public static boolean verifySignature(Map<String, String> params, String secretKey) {
        String providedSign = params.get("sign");
        if (!StringUtils.hasText(providedSign)) {
            return false;
        }
        
        // 生成簽名進行比對
        String generatedSign = generateSignature(params, secretKey);
        return providedSign.equals(generatedSign);
    }
    
    /**
     * 驗證時間戳是否有效(防重放攻擊)
     * @param timestamp 時間戳
     * @param timeWindow 有效時間窗口(毫秒)
     * @return 是否在有效期內(nèi)
     */
    public static boolean isTimestampValid(String timestamp, long timeWindow) {
        try {
            long requestTime = Long.parseLong(timestamp);
            long currentTime = System.currentTimeMillis();
            return Math.abs(currentTime - requestTime) <= timeWindow;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}

2. 簽名驗證攔截器

@Component
public class SignatureVerificationInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(SignatureVerificationInterceptor.class);
    
    @Value("${api.signature.secret}")
    private String secretKey;
    
    @Value("${api.signature.time-window}")
    private long timeWindow;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 檢查是否需要驗證簽名的接口
        if (isSignatureRequired(request, handler)) {
            Map<String, String> params = getAllParameters(request);
            
            // 驗證時間戳
            String timestamp = params.get("timestamp");
            if (!SignatureUtils.isTimestampValid(timestamp, timeWindow)) {
                logger.warn("Invalid timestamp: {}", timestamp);
                response.setStatus(HttpStatus.FORBIDDEN.value());
                response.getWriter().write("{"code":403,"message":"Invalid timestamp"}");
                return false;
            }
            
            // 驗證簽名
            if (!SignatureUtils.verifySignature(params, secretKey)) {
                logger.warn("Invalid signature for request: {}", request.getRequestURI());
                response.setStatus(HttpStatus.FORBIDDEN.value());
                response.getWriter().write("{"code":403,"message":"Invalid signature"}");
                return false;
            }
        }
        
        return true;
    }
    
    private boolean isSignatureRequired(HttpServletRequest request, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 檢查是否有SignatureRequired注解
            return handlerMethod.getMethod().isAnnotationPresent(SignatureRequired.class);
        }
        return false;
    }
    
    private Map<String, String> getAllParameters(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        
        // 獲取URL參數(shù)
        request.getParameterMap().forEach((key, values) -> {
            if (values != null && values.length > 0) {
                params.put(key, values[0]);
            }
        });
        
        // 如果是POST請求且Content-Type為application/json,解析請求體
        if ("POST".equals(request.getMethod()) && 
            request.getContentType() != null && 
            request.getContentType().contains("application/json")) {
            try {
                String body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
                if (StringUtils.hasText(body)) {
                    ObjectMapper mapper = new ObjectMapper();
                    JsonNode jsonNode = mapper.readTree(body);
                    // 將JSON轉(zhuǎn)換為平鋪的Map
                    flattenJsonToMap("", jsonNode, params);
                }
            } catch (IOException e) {
                logger.error("Failed to parse request body", e);
            }
        }
        
        return params;
    }
    
    private void flattenJsonToMap(String prefix, JsonNode jsonNode, Map<String, String> map) {
        if (jsonNode.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> entry = fields.next();
                String newPrefix = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey();
                flattenJsonToMap(newPrefix, entry.getValue(), map);
            }
        } else if (jsonNode.isArray()) {
            // 簡單處理:數(shù)組轉(zhuǎn)為逗號分隔字符串
            StringBuilder sb = new StringBuilder();
            for (JsonNode element : jsonNode) {
                if (sb.length() > 0) {
                    sb.append(",");
                }
                sb.append(element.asText());
            }
            map.put(prefix, sb.toString());
        } else {
            map.put(prefix, jsonNode.asText());
        }
    }
}

3. 自定義注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SignatureRequired {
}

4. 配置攔截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private SignatureVerificationInterceptor signatureVerificationInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signatureVerificationInterceptor)
                .addPathPatterns("/api/**");
    }
}

5. 接口使用示例

@RestController
@RequestMapping("/api/open")
public class OpenApiController {

    @GetMapping("/public-data")
    public ResponseEntity<String> publicData() {
        return ResponseEntity.ok("This is public data without signature verification");
    }
    
    @SignatureRequired
    @GetMapping("/protected-data")
    public ResponseEntity<String> protectedData() {
        return ResponseEntity.ok("This is protected data with signature verification");
    }
}

優(yōu)缺點分析

優(yōu)點:

• 無需用戶交互,適合系統(tǒng)間API調(diào)用

• 可防止請求篡改和重放攻擊

• 不依賴于會話和Cookie

• 可與其他安全機制結(jié)合使用

缺點:

• 接口調(diào)用較為復(fù)雜,需客戶端配合實現(xiàn)簽名邏輯

• 時鐘不同步可能導(dǎo)致驗證失敗

• 密鑰泄露風(fēng)險高,需妥善保管

適用場景

• 開放API平臺

• 支付和金融相關(guān)接口

• 系統(tǒng)間后臺通信

• 對安全性要求高的數(shù)據(jù)交換接口

方法五:限流與防刷機制

原理

限流與防刷機制通過控制API訪問頻率,防止惡意用戶通過高頻請求對系統(tǒng)進行攻擊或爬取數(shù)據(jù)。常見的限流算法包括固定窗口計數(shù)器、滑動窗口、漏桶和令牌桶等。

實現(xiàn)方式

1. 添加依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

2. 基于Redis的限流實現(xiàn)

@Component
public class RedisRateLimiter {

    private final StringRedisTemplate redisTemplate;
    
    @Autowired
    public RedisRateLimiter(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 固定窗口限流算法
     * @param key 限流鍵
     * @param period 窗口期(秒)
     * @param maxRequests 最大請求數(shù)
     * @return 是否允許請求
     */
    public boolean isAllowed(String key, int period, int maxRequests) {
        String finalKey = "rate:limiter:" + key;
        
        // 獲取當前計數(shù)
        Long count = redisTemplate.opsForValue().increment(finalKey, 1);
        
        // 如果是第一次請求,設(shè)置過期時間
        if (count != null && count == 1) {
            redisTemplate.expire(finalKey, period, TimeUnit.SECONDS);
        }
        
        return count != null && count <= maxRequests;
    }
}

3. 基于Guava的令牌桶限流器

@Component
public class GuavaRateLimiter {

    private final ConcurrentMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();
    
    /**
     * 獲取指定key的限流器
     * @param key 限流鍵
     * @param permitsPerSecond 每秒允許的請求數(shù)
     * @return RateLimiter實例
     */
    public RateLimiter getRateLimiter(String key, double permitsPerSecond) {
        return limiters.computeIfAbsent(key, k -> RateLimiter.create(permitsPerSecond));
    }
    
    /**
     * 嘗試獲取令牌
     * @param key 限流鍵
     * @param permitsPerSecond 每秒允許的請求數(shù)
     * @return 是否獲取成功
     */
    public boolean tryAcquire(String key, double permitsPerSecond) {
        RateLimiter limiter = getRateLimiter(key, permitsPerSecond);
        return limiter.tryAcquire();
    }
    
    /**
     * 嘗試在指定時間內(nèi)獲取令牌
     * @param key 限流鍵
     * @param permitsPerSecond 每秒允許的請求數(shù)
     * @param timeout 超時時間
     * @param unit 時間單位
     * @return 是否獲取成功
     */
    public boolean tryAcquire(String key, double permitsPerSecond, long timeout, TimeUnit unit) {
        RateLimiter limiter = getRateLimiter(key, permitsPerSecond);
        return limiter.tryAcquire(1, timeout, unit);
    }
}

4. IP限流攔截器

@Component
public class IpRateLimitInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(IpRateLimitInterceptor.class);
    
    @Autowired
    private RedisRateLimiter redisRateLimiter;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            
            // 檢查是否有RateLimit注解
            RateLimit rateLimit = handlerMethod.getMethod().getAnnotation(RateLimit.class);
            if (rateLimit != null) {
                String ip = getClientIp(request);
                String uri = request.getRequestURI();
                String key = ip + ":" + uri;
                
                if (!redisRateLimiter.isAllowed(key, rateLimit.period(), rateLimit.maxRequests())) {
                    logger.warn("Rate limit exceeded for IP: {}, URI: {}", ip, uri);
                    response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
                    response.getWriter().write("{"code":429,"message":"Too many requests"}");
                    return false;
                }
            }
        }
        
        return true;
    }
    
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 如果是多級代理,取第一個IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return ip;
    }
}

5. 自定義限流注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    
    /**
     * 窗口期(秒)
     */
    int period() default 60;
    
    /**
     * 窗口期內(nèi)最大請求數(shù)
     */
    int maxRequests() default 10;
}

6. 限流注解全局處理(AOP方式)

@Aspect
@Component
public class RateLimitAspect {

    private static final Logger logger = LoggerFactory.getLogger(RateLimitAspect.class);
    
    @Autowired
    private GuavaRateLimiter guavaRateLimiter;
    
    @Autowired
    private HttpServletRequest request;
    
    @Around("@annotation(rateLimit)")
    public Object rateLimit(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        String methodName = point.getSignature().getName();
        String className = point.getTarget().getClass().getName();
        String ip = getClientIp(request);
        
        // 使用方法名、類名和IP作為限流鍵
        String key = ip + ":" + className + ":" + methodName;
        
        // 限流速率:maxRequests / period
        double permitsPerSecond = (double) rateLimit.maxRequests() / rateLimit.period();
        
        if (guavaRateLimiter.tryAcquire(key, permitsPerSecond)) {
            return point.proceed();
        } else {
            logger.warn("Rate limit exceeded for method: {}.{}, IP: {}", className, methodName, ip);
            throw new TooManyRequestsException("Too many requests, please try again later.");
        }
    }
    
    private String getClientIp(HttpServletRequest request) {
        // 獲取客戶端IP的邏輯,同上文
    }
}

7. 配置攔截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private IpRateLimitInterceptor ipRateLimitInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(ipRateLimitInterceptor)
                .addPathPatterns("/api/**");
    }
}

8. 使用限流注解的控制器

@RestController
@RequestMapping("/api")
public class RateLimitedController {

    @RateLimit(period = 60, maxRequests = 5)
    @GetMapping("/limited-data")
    public ResponseEntity<String> getLimitedData() {
        return ResponseEntity.ok("This is rate-limited data");
    }
    
    @RateLimit(period = 3600, maxRequests = 100)
    @PostMapping("/submit-form")
    public ResponseEntity<String> submitForm(@RequestBody FormData formData) {
        return ResponseEntity.ok("Form submitted successfully");
    }
}

優(yōu)缺點分析

優(yōu)點:

• 有效防止惡意攻擊和刷接口行為

• 保護系統(tǒng)資源,防止過載

• 可針對不同用戶、IP或接口設(shè)置不同限制

• 結(jié)合業(yè)務(wù)場景靈活配置

缺點:

• 分布式環(huán)境下需要集中存儲計數(shù)器(如Redis)

• 時間窗口設(shè)置不當可能影響正常用戶體驗

• 固定限流策略難以應(yīng)對突發(fā)流量

適用場景

• 防止接口被惡意調(diào)用或爬取

• 保護高消耗資源的接口

• 付費API的訪問頻率控制

• 防止用戶批量操作(如注冊、評論等)

方案比較

方法復(fù)雜度安全性適用場景主要優(yōu)勢主要劣勢
Spring Security認證授權(quán)企業(yè)應(yīng)用、內(nèi)部系統(tǒng)全面的安全框架,細粒度權(quán)限控制配置復(fù)雜,學(xué)習(xí)曲線陡
JWT無狀態(tài)認證前后端分離、微服務(wù)無狀態(tài),易于擴展Token撤銷困難
OAuth 2.0授權(quán)第三方登錄、API平臺標準化授權(quán)協(xié)議,支持多種授權(quán)模式實現(xiàn)復(fù)雜
接口簽名驗證開放API、支付接口防篡改,適合系統(tǒng)間調(diào)用需客戶端配合實現(xiàn)
限流防刷機制防爬蟲、資源保護簡單有效,保護系統(tǒng)資源可能影響正常用戶

總結(jié)

在實際應(yīng)用中,往往需要根據(jù)具體場景組合使用這些方法,構(gòu)建多層次的安全防護體系。

同時,安全是一個持續(xù)的過程,除了技術(shù)手段外,還需要定期的安全審計、漏洞掃描和安全意識培訓(xùn)。

到此這篇關(guān)于SpringBoot中接口安全的5種訪問控制方法詳解的文章就介紹到這了,更多相關(guān)SpringBoot接口安全的訪問控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring-cloud入門之eureka-client(服務(wù)注冊)

    spring-cloud入門之eureka-client(服務(wù)注冊)

    本篇文章主要介紹了spring-cloud入門之eureka-client(服務(wù)注冊),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 關(guān)于SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題

    關(guān)于SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題

    這篇文章主要介紹了SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • Java如何接收并解析HL7協(xié)議數(shù)據(jù)

    Java如何接收并解析HL7協(xié)議數(shù)據(jù)

    文章主要介紹了HL7協(xié)議及其在醫(yī)療行業(yè)中的應(yīng)用,詳細描述了如何配置環(huán)境、接收和解析數(shù)據(jù),以及與前端進行交互的實現(xiàn)方法,文章還分享了使用7Edit工具進行調(diào)試的經(jīng)驗,并記錄了一個常見的解析問題及其解決方法
    2024-12-12
  • 基于Java編寫一個神奇的代碼恢復(fù)工具

    基于Java編寫一個神奇的代碼恢復(fù)工具

    這篇文章主要為大家詳細介紹了如何基于Java編寫一個神奇的代碼恢復(fù)工具,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • java中建立0-10m的消息(字符串)實現(xiàn)方法

    java中建立0-10m的消息(字符串)實現(xiàn)方法

    下面小編就為大家?guī)硪黄猨ava中建立0-10m的消息(字符串)實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java實現(xiàn)調(diào)用MySQL存儲過程詳解

    Java實現(xiàn)調(diào)用MySQL存儲過程詳解

    相信大家都知道存儲過程是在大型數(shù)據(jù)庫系統(tǒng)中,一組為了完成特定功能的SQL語句集。存儲過程是數(shù)據(jù)庫中的一個重要對象,任何一個設(shè)計良好的數(shù)據(jù)庫應(yīng)用程序都應(yīng)該用到存儲過程。Java調(diào)用mysql存儲過程,實現(xiàn)如下,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • springboot集成websocket的四種方式小結(jié)

    springboot集成websocket的四種方式小結(jié)

    本文主要介紹了springboot集成websocket的四種方式小結(jié),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • IDEA中實現(xiàn)springboot熱部署方式

    IDEA中實現(xiàn)springboot熱部署方式

    在IDEA中實現(xiàn)SpringBoot的熱部署可以通過修改設(shè)置來完成,首先在設(shè)置中搜索Compiler,并勾選Build project automatically,然后進入Advanced Settings,勾選Allow auto-make to start even if developed application is currently running
    2024-09-09
  • java接口防重提交的處理方法

    java接口防重提交的處理方法

    本文主要介紹了java接口防重提交的處理方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 詳解Java如何實現(xiàn)防止惡意注冊

    詳解Java如何實現(xiàn)防止惡意注冊

    惡意注冊通常是指使用自動化腳本或者機器人在短時間內(nèi)進行大量的注冊行為,這種行為會對系統(tǒng)造成壓力,甚至?xí)?dǎo)致系統(tǒng)癱瘓。所以本文為大家總結(jié)了一些防止惡意注冊的方法,需要的可以參考一下
    2023-04-04

最新評論

红原县| 营山县| 理塘县| 乌兰浩特市| 六枝特区| 关岭| 斗六市| 蒲城县| 长沙市| 滦平县| 什邡市| 灵璧县| 新龙县| 东丽区| 青岛市| 大同县| 遂宁市| 德惠市| 宜昌市| 双桥区| 佳木斯市| 南漳县| 兴文县| 迭部县| 泰宁县| 灌阳县| 高阳县| 昔阳县| 巴林左旗| 通州市| 吉安市| 铜鼓县| 莒南县| 陕西省| 泌阳县| 永福县| 远安县| 进贤县| 金坛市| 津南区| 富源县|