{"id":26066221,"url":"https://github.com/shade-sdev/shade-spring-boot-starter","last_synced_at":"2025-03-08T20:11:47.756Z","repository":{"id":281132313,"uuid":"862201106","full_name":"shade-sdev/shade-spring-boot-starter","owner":"shade-sdev","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-07T06:01:42.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-07T07:19:38.743Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shade-sdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-24T07:55:27.000Z","updated_at":"2025-03-07T06:01:46.000Z","dependencies_parsed_at":"2025-03-07T07:19:40.861Z","dependency_job_id":"ce57f014-27d4-4aac-a240-94bc5ab47555","html_url":"https://github.com/shade-sdev/shade-spring-boot-starter","commit_stats":null,"previous_names":["shade-sdev/shade-spring-boot-starter"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shade-sdev%2Fshade-spring-boot-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shade-sdev%2Fshade-spring-boot-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shade-sdev%2Fshade-spring-boot-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shade-sdev%2Fshade-spring-boot-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shade-sdev","download_url":"https://codeload.github.com/shade-sdev/shade-spring-boot-starter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242602287,"owners_count":20156376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-08T20:11:46.966Z","updated_at":"2025-03-08T20:11:47.750Z","avatar_url":"https://github.com/shade-sdev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shade Starter Security\n\n**Shade Starter Security** is a Spring Boot starter that provides rate limiting with caching features, allowing you to control access to your application’s methods based on user roles and request\nlimits.\n\n## Dependency\n\nTo use Shade Starter Security in your Spring Boot project, add the following dependency to your `pom.xml`:\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.shade-sdev\u003c/groupId\u003e\n    \u003cartifactId\u003eshade-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003emaster-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Features\n\n- **Rate Limiting**: Annotate methods to enforce request limits.\n\n  You can use the `@RateLimit` annotation to specify the maximum number of requests allowed and the time frame for that limit. Here’s an example:\n\n    ```java\n    @RateLimit(maxRequests = 5, time = 1, timeUnit = TimeUnit.HOURS)\n    public void someRateLimitedMethod() {\n        // Method implementation\n    }\n    ```\n\n- **Role-Based Access**: Define which user roles can access rate-limited methods.\n\n  The `@RateLimit` annotation allows you to specify roles that are permitted to access the method. For example:\n\n    ```java\n    @RateLimit(maxRequests = 3, time = 10, timeUnit = TimeUnit.MINUTES, roles = {\"ROLE_ADMIN\", \"ROLE_USER\"})\n    public String accessRestrictedResource() {\n        return \"Access granted to restricted resource.\";\n    }\n    ```\n\n- **Caching Support**: Utilizes caching to manage rate limit counters efficiently.\n\n  The implementation uses a caching mechanism to store and retrieve rate limit counters. Here’s an example of how to implement a simple caching service:\n  Make sure to enable Caching with `@EnableCaching`\n    ```java\n    @Component\n    public class CacheService {\n\n        private static final String RATE_CACHE = \"rateLimit\";\n\n        private final CacheManager cacheManager;\n\n        @Autowired\n        public CacheService(CacheManager cacheManager) {\n            this.cacheManager = cacheManager;\n        }\n\n        public void putCounter(String key, RateLimitCounter value) {\n            Optional.ofNullable(cacheManager.getCache(RATE_CACHE)).ifPresent(c -\u003e c.put(key, value));\n        }\n\n        public RateLimitCounter getCounter(String key) {\n            return Optional.ofNullable(cacheManager.getCache(RATE_CACHE))\n                           .map(cache -\u003e cache.get(key))\n                           .map(Cache.ValueWrapper::get)\n                           .map(RateLimitCounter.class::cast)\n                           .orElse(null);\n        }\n    }\n    ```\n\n## RateLimitException\n\nThe `RateLimitException` is thrown when a user exceeds the allowed number of requests within a specified time frame. This exception is used to manage rate limit violations and provide appropriate\nfeedback to the user.\n\n### Usage\n\nWhen a method annotated with `@RateLimit` is accessed, and the rate limit has been exceeded, the `RateLimitException` will be triggered. You can handle this exception globally to return a\nuser-friendly message or status code.\n\n### Example\n\n```java\n\n@ControllerAdvice\npublic class GlobalExceptionHandler {\n\n    @ExceptionHandler(RateLimitException.class)\n    @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)\n    public String handleRateLimitException(RateLimitException e) {\n        return e.getMessage(); // Return the exception message to the user\n    }\n}\n```\n\n- **Permission Evaluator Factory**: Create multiple Permission Evaluator for a specific class\n\nThis system allows you to manage permission evaluation in a flexible way based on the type of the target object. It leverages Spring Security's `PermissionEvaluator` interface and enhances it by\nintroducing a `TargetedPermissionEvaluator` interface for specific object types.\n\n### Components\n\nTargetedPermissionEvaluator\nAn interface that extends Spring Security's PermissionEvaluator:\n\n```java\npublic interface TargetedPermissionEvaluator extends PermissionEvaluator {\n    String getTargetType();\n}\n```\n\ngetTargetType(): Returns the fully qualified class name of the target type this evaluator handles.\n\nPermissionEvaluatorManager\nManages and retrieves the appropriate TargetedPermissionEvaluator for a given class name:\n\n```java\n\n@Component\npublic class PermissionEvaluatorManager {\n    // ... (constructor and fields omitted for brevity)\n\n    public PermissionEvaluator targetedPermissionEvaluator(String className) {\n        // ... (implementation details)\n    }\n}\n```\n\nUses Spring's ApplicationContext to fetch all beans of type TargetedPermissionEvaluator.\nReturns the appropriate evaluator based on the provided class name, or a DenyAllPermissionEvaluator if none is found.\n\nMainPermissionEvaluator\nThe main entry point for permission evaluation:\n\n```java\npublic class MainPermissionEvaluator implements PermissionEvaluator {\n    // ... (constructor and fields omitted for brevity)\n\n    @Override\n    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n        // ... (implementation details)\n    }\n\n    @Override\n    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {\n        // ... (implementation details)\n    }\n}\n```\n\nDelegates to the appropriate TargetedPermissionEvaluator based on the target object's class or provided target type.\nFalls back to DenyAllPermissionEvaluator if no specific evaluator is found.\n\nDenyAllPermissionEvaluator\nA fallback permission evaluator that denies all permissions:\n\n```java\npublic class DenyAllPermissionEvaluator implements TargetedPermissionEvaluator {\n    // ... (implementation details)\n}\n```\n\nAlways returns false for any permission check.\nUsed as a default when no specific evaluator is found.\n\n### Usage\n\nCreate custom permission evaluators by implementing TargetedPermissionEvaluator:\n\n```java\n\n@Component\npublic class TestPermissionEvaluator implements TargetedPermissionEvaluator {\n    @Override\n    public String getTargetType() {\n        return Person.class.getName();\n    }\n\n    // ... (implement hasPermission methods)\n}\n```\n\nUse the @PreAuthorize annotation with the hasPermission expression:\n\n```java\nimport org.springframework.http.ResponseEntity;\n\n@PreAuthorize(\"hasPermission(null, 'com.example.person', null)\")\npublic ResponseEntity\u003cVoid\u003e deletePerson() {\n    // ...\n}\n```\n\nApplying it to your own MethodSecurityExpressionHandler\n\n```java\n\n@Bean\npublic PermissionEvaluatorManager permissionEvaluatorManager(ApplicationContext applicationContext) {\n    return new PermissionEvaluatorManager(applicationContext);\n}\n\n@Bean\npublic MethodSecurityExpressionHandler methodSecurityExpressionHandler(\n        RoleHierarchy roleHierarchy,\n        PermissionEvaluatorManager permissionEvaluatorManager\n) {\n    DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();\n    expressionHandler.setRoleHierarchy(roleHierarchy);\n    expressionHandler.setPermissionEvaluator(new MainPermissionEvaluator(permissionEvaluatorManager));\n    return expressionHandler;\n}\n```\n\nHow It Works\n\nWhen a method annotated with @PreAuthorize is called, Spring Security invokes the MainPermissionEvaluator.\nMainPermissionEvaluator uses PermissionEvaluatorManager to find the appropriate TargetedPermissionEvaluator.\nIf a matching evaluator is found, it's used to check the permission.\nIf no matching evaluator is found, the DenyAllPermissionEvaluator is used, denying the permission.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshade-sdev%2Fshade-spring-boot-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshade-sdev%2Fshade-spring-boot-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshade-sdev%2Fshade-spring-boot-starter/lists"}