From a1bc1d8cbd230ef768641d62b97fdfdd808bcd36 Mon Sep 17 00:00:00 2001 From: "Yangkai.Shen" <237497819@qq.com> Date: Mon, 17 Dec 2018 19:09:27 +0800 Subject: [PATCH] =?UTF-8?q?:memo:=20=E6=9B=B4=E6=96=B0=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-boot-demo-rbac-security/README.md | 188 ++++++++++++++++++++--- 1 file changed, 167 insertions(+), 21 deletions(-) diff --git a/spring-boot-demo-rbac-security/README.md b/spring-boot-demo-rbac-security/README.md index 611a0bc..b97e3fe 100644 --- a/spring-boot-demo-rbac-security/README.md +++ b/spring-boot-demo-rbac-security/README.md @@ -11,6 +11,7 @@ - [x] 使用 `JWT` 实现安全验证,同时引入 `Redis` 解决 `JWT` 无法手动设置过期的弊端,并且保证同一用户在同一时间仅支持同一设备登录,不同设备登录会将,详情参考 [`JwtUtil.java`](./src/main/java/com/xkcoding/rbac/security/util/JwtUtil.java) - [x] 在线人数统计,详情参考 [`MonitorService.java`](./src/main/java/com/xkcoding/rbac/security/service/MonitorService.java) 和 [`RedisUtil.java`](./src/main/java/com/xkcoding/rbac/security/util/RedisUtil.java) - [x] 手动踢出用户,详情参考 [`MonitorService.java`](./src/main/java/com/xkcoding/rbac/security/service/MonitorService.java) +- [x] 自定义配置不需要进行拦截的请求,详情参考 [`CustomConfig.java`](./src/main/java/com/xkcoding/rbac/security/config/CustomConfig.java) 和 [`application.yml`](./src/main/resources/application.yml) ## 2. 运行 @@ -313,7 +314,11 @@ public class JwtUtil { */ @Configuration @EnableWebSecurity +@EnableConfigurationProperties(CustomConfig.class) public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private CustomConfig customConfig; + @Autowired private AccessDeniedHandler accessDeniedHandler; @@ -357,9 +362,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { // 认证请求 .authorizeRequests() - // 放行 /api/auth/** 的所有请求,参见 AuthController - .antMatchers("/**/api/auth/**") - .permitAll() + // 所有请求都需要登录访问 .anyRequest() .authenticated() // RBAC 动态 url 认证 @@ -368,7 +371,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { // 登出行为由自己实现,参考 AuthController#logout .and() - .logout().disable() + .logout() + .disable() // Session 管理 .sessionManagement() @@ -383,6 +387,72 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { // 添加自定义 JWT 过滤器 http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } + + /** + * 放行所有不需要登录就可以访问的请求,参见 AuthController + * 也可以在 {@link #configure(HttpSecurity)} 中配置 + * {@code http.authorizeRequests().antMatchers("/api/auth/**").permitAll()} + */ + @Override + public void configure(WebSecurity web) { + WebSecurity and = web.ignoring() + .and(); + + // 忽略 GET + customConfig.getIgnores() + .getGet() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.GET, url)); + + // 忽略 POST + customConfig.getIgnores() + .getPost() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.POST, url)); + + // 忽略 DELETE + customConfig.getIgnores() + .getDelete() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.DELETE, url)); + + // 忽略 PUT + customConfig.getIgnores() + .getPut() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.PUT, url)); + + // 忽略 HEAD + customConfig.getIgnores() + .getHead() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.HEAD, url)); + + // 忽略 PATCH + customConfig.getIgnores() + .getPatch() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.PATCH, url)); + + // 忽略 OPTIONS + customConfig.getIgnores() + .getOptions() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.OPTIONS, url)); + + // 忽略 TRACE + customConfig.getIgnores() + .getTrace() + .forEach(url -> and.ignoring() + .antMatchers(HttpMethod.TRACE, url)); + + // 按照请求格式忽略 + customConfig.getIgnores() + .getPattern() + .forEach(url -> and.ignoring() + .antMatchers(url)); + + } } ``` @@ -518,7 +588,10 @@ public class RbacAuthorityService { ### 3.5. JwtAuthenticationFilter.java -> JWT 认证过滤器,主要功能:根据当前请求的JWT,认证用户身份信息 +> JWT 认证过滤器,主要功能: +> +> 1. 过滤不需要拦截的请求 +> 2. 根据当前请求的JWT,认证用户身份信息 ```java /** @@ -543,32 +616,105 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; + @Autowired + private CustomConfig customConfig; + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - AntPathMatcher antPathMatcher = new AntPathMatcher(); - if (antPathMatcher.match("/**/api/auth/**", request.getRequestURI())) { + + if (checkIgnores(request)) { filterChain.doFilter(request, response); + return; + } + + String jwt = jwtUtil.getJwtFromRequest(request); + + if (StrUtil.isNotBlank(jwt)) { + try { + String username = jwtUtil.getUsernameFromJWT(jwt); + + UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext() + .setAuthentication(authentication); + filterChain.doFilter(request, response); + } catch (SecurityException e) { + ResponseUtil.renderJson(response, e); + } } else { - String jwt = jwtUtil.getJwtFromRequest(request); + ResponseUtil.renderJson(response, Status.UNAUTHORIZED, null); + } - if (StrUtil.isNotBlank(jwt)) { - try { - String username = jwtUtil.getUsernameFromJWT(jwt); + } - UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + /** + * 请求是否不需要进行权限拦截 + * + * @param request 当前请求 + * @return true - 忽略,false - 不忽略 + */ + private boolean checkIgnores(HttpServletRequest request) { + String method = request.getMethod(); + + HttpMethod httpMethod = HttpMethod.resolve(method); + if (ObjectUtil.isNull(httpMethod)) { + httpMethod = HttpMethod.GET; + } - SecurityContextHolder.getContext() - .setAuthentication(authentication); - filterChain.doFilter(request, response); - } catch (SecurityException e) { - ResponseUtil.renderJson(response, e); + Set ignores = Sets.newHashSet(); + + switch (httpMethod) { + case GET: + ignores.addAll(customConfig.getIgnores() + .getGet()); + break; + case PUT: + ignores.addAll(customConfig.getIgnores() + .getPut()); + break; + case HEAD: + ignores.addAll(customConfig.getIgnores() + .getHead()); + break; + case POST: + ignores.addAll(customConfig.getIgnores() + .getPost()); + break; + case PATCH: + ignores.addAll(customConfig.getIgnores() + .getPatch()); + break; + case TRACE: + ignores.addAll(customConfig.getIgnores() + .getTrace()); + break; + case DELETE: + ignores.addAll(customConfig.getIgnores() + .getDelete()); + break; + case OPTIONS: + ignores.addAll(customConfig.getIgnores() + .getOptions()); + break; + default: + break; + } + + ignores.addAll(customConfig.getIgnores() + .getPattern()); + + if (CollUtil.isNotEmpty(ignores)) { + for (String ignore : ignores) { + AntPathRequestMatcher matcher = new AntPathRequestMatcher(ignore, method); + if (matcher.matches(request)) { + return true; } - } else { - ResponseUtil.renderJson(response, Status.UNAUTHORIZED, null); } } + + return false; } }