Spring Security 安全校验前后端分离

小小小幸运  金牌会员 | 2024-9-21 17:28:47 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 840|帖子 840|积分 2520

Spring Security 是一个专注于向 Java 应用程序提供身份验证和授权的安全框架,在Web情况下,它是借助Filter来实现对哀求的校验; 因为是一个框架,开发出来的目标是为了适配各个不同的场景,各种扩展,再加上框架本身默认的功能是在以template 画html, 以Session做回话管理这种开发模式; 不过我们现在都是前后端分离,以是原有的一些功能就不怎么实用了,导致我们刚接手时会觉得有点困难,接下来我们简朴讲解一下框架的流程,以及后续更改为前后端使用Token交互的方式;
官方的中文翻译
DelegatingFilterProxy 负责FilterBean的延迟加载(忽略不用管);

FilterChainProxy 内部委托给 List filterChains进行处理惩罚

SecurityFilterChain 对Url 进行匹配,匹配通过, 使用内部的Filter进行处理惩罚
   org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@50f097b5,
将 Security 上下文与 Spring Web 中用于处理惩罚异步哀求映射的 WebAsyncManager 进行集成。
org.springframework.security.web.context.SecurityContextHolderFilter@6fc6deb7,
用于生成 SecurityContextHolder
org.springframework.security.web.header.HeaderWriterFilter@6f76c2cc,
获取定义的HeaderWriter 对象,对哀求头进行write
org.springframework.security.web.csrf.CsrfFilter@c29fe36,
处理惩罚Csrf攻击
org.springframework.security.web.authentication.logout.LogoutFilter@ef60710,
登出操作
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7c2dfa2,
定义登录使用的Url 各种参数如何获取,生成UsernamePasswordAuthenticationToken 委托给AuthenticationManager 进行鉴权;
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@4397a639,
提供一个登录一个页面, 忽略
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7add838c,
登出页面, 忽略
org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5cc9d3d0,
提供基于Basic 的登录校验
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7da39774,
哀求缓存, 用于访问非登录接口后重定向到登录接口 ,并在登录成功跳回原接口;
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@32b0876c,
用来创建 Servlet3SecurityContextHolderAwareRequestWrapper ,主要是是Servlet 的鉴权体系与Spring整合到一起, 忽略
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3662bdff,
创建匿名用户, 项目上除非有特殊需求, 这个也可以忽略
org.springframework.security.web.access.ExceptionTranslationFilter@77681ce4,
对鉴权失败的异常处理惩罚, 也可忽略, 只要知道在鉴权认证那步会抛出哪些异常就可以
org.springframework.security.web.access.intercept.AuthorizationFilter@169268a7]
权限判定, 哪些接口有权限访问,哪些接口没有权限访问之类的
  对于安全方面,还涉及到一些Http 哀求头的安全参数, 具体的可以看代码;
改为Token 登录;
  1.             <groupId>com.auth0</groupId>
  2.             <artifactId>java-jwt</artifactId>
  3.         </dependency>
  4.         <dependency>
  5.             <groupId>com.auth0</groupId>
  6.             <artifactId>jwks-rsa</artifactId>
  7.         </dependency>
  8.         <dependency>
  9.             <groupId>commons-collections</groupId>
  10.             <artifactId>commons-collections</artifactId>
  11.             <version>3.2</version>
  12.         </dependency>
  13.         <dependency>
  14.             <groupId>org.springframework.boot</groupId>
  15.             <artifactId>spring-boot-starter-security</artifactId>
  16.         </dependency>
复制代码
  1. @Bean
  2.     protected SecurityFilterChain filterChain(HttpSecurity httpSecurity, JWTParseFilter jwtParseFilter,
  3.                                               AccountTokenAuthenticationFilter accountTokenAuthenticationFilter,
  4.                                               List<Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>> customizer,
  5.                                               DBAuthorizationManager dbAuthorizationManager) throws Exception {
  6.         httpSecurity
  7.                 // 跨域
  8.                 .cors(Customizer.withDefaults())
  9.                 // CSRF 禁用
  10.                 .csrf(AbstractHttpConfigurer::disable)
  11.                 .sessionManagement(AbstractHttpConfigurer::disable)
  12.                 .requestCache(RequestCacheConfigurer::disable)
  13. //                .anonymous(AbstractHttpConfigurer::disable)
  14.                 .headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
  15.                 .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
  16.                         .accessDeniedHandler(accessDeniedHandler));
  17.         // 设置每个请求的权限
  18.         httpSecurity.authorizeHttpRequests(
  19.                 registry ->
  20.                 {
  21.                     customizer.forEach(e -> e.customize(registry));
  22.                     registry.anyRequest().authenticated();
  23.                 }
  24.         );
  25.         // 添加 Token Filter
  26.         httpSecurity.addFilterAfter(jwtParseFilter, LogoutFilter.class);
  27.         accountTokenAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
  28.         accountTokenAuthenticationFilter.setAuthenticationFailureHandler(failureHandler);
  29.         httpSecurity.addFilterAfter(accountTokenAuthenticationFilter, JWTParseFilter.class);
  30.         httpSecurity.addFilterAt(new AuthorizationFilter(dbAuthorizationManager), AuthorizationFilter.class);
  31.         return httpSecurity.build();
  32.     }
复制代码
JWTParseFilter 主要是检查token 是否存在, 存在的话则进行校验检测,判定是不是我们的自己生成的, 检测通事后,将token 转换为UsernamePasswordAuthenticationToken 对象传入上下文
  1.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  2.         String token = getToken(request);
  3.         log.debug("JWTParseFilter.doFilterInternal  token {}", token);
  4.         if (StringUtils.isNotEmpty(token)) {
  5.             validToken(request, token);
  6.         }
  7.         // 继续过滤链
  8.         filterChain.doFilter(request, response);
  9.     }
  10.     private void validToken(HttpServletRequest request, String token) {
  11.         DecodedJWT jwt = JWTHandler.parseToken(token);
  12.         log.debug("JWTParseFilter.doFilterInternal jwt {}", jwt);
  13.         String userName = jwt.getClaim("user_name").asString();
  14.         String roles = jwt.getClaim("roles").asString();
  15.         List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
  16.         if (StringUtils.isNotBlank(roles)) {
  17.             String[] split = roles.split(",");
  18.             for (String role : split) {
  19.                 grantedAuthorities.add(new SimpleGrantedAuthority(role));
  20.             }
  21.         }
  22.         User user = new User(userName, "", grantedAuthorities);
  23.         // 创建 Authentication,并设置到上下文
  24.         UsernamePasswordAuthenticationToken authenticationToken =
  25.                 UsernamePasswordAuthenticationToken.authenticated(
  26.                         user, null, Collections.emptyList());
  27.         authenticationToken.setDetails(new WebAuthenticationDetails(request));
  28.         SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  29.     }
复制代码
生成token
  1. @Slf4j
  2. @Component
  3. public class AccountTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  4.     public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
  5.     public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
  6.     private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/user/login",
  7.             "POST");
  8.     public AccountTokenAuthenticationFilter(AuthenticationManager authenticationManager) {
  9.         super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
  10.     }
  11.     @Override
  12.     public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  13.             throws AuthenticationException, IOException {
  14.         ServletInputStream inputStream = request.getInputStream();
  15.         String jsonStr = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  16.         JSONObject obj = JSON.parseObject(jsonStr);
  17.         Object username = obj.get(SPRING_SECURITY_FORM_USERNAME_KEY);
  18.         username = (username != null) ? username.toString().trim() : "";
  19.         Object password = obj.get(SPRING_SECURITY_FORM_PASSWORD_KEY);
  20.         password = (password != null) ? password.toString().trim() : "";
  21.         UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
  22.                 password);
  23.         authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
  24.         return this.getAuthenticationManager().authenticate(authRequest);
  25.     }
  26. }
复制代码
DBAuthorizationManager 主要负责对Url 进行权限认证, 实际使用的时间使用数据库设置的数据作为数据源, 这样在使用的时间,直接改数据库就可以了, 不要使用注解,后期该权限就得发布, 走审批走CD ,流程很繁琐;
  1. @Component
  2. public class DBAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
  3.     Map<String, List<String>> urlAuth = Map.of("/security/test1/success", List.of("admin"));
  4.     @Override
  5.     public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest object) {
  6.         String uri = object.getRequestURI();
  7.         List<String> strings = urlAuth.get(uri);
  8.         if (strings == null) {
  9.             return new AuthorizationDecision(true);
  10.         }
  11.         if (strings.contains(uri)) {
  12.             return new AuthorizationDecision(true);
  13.         } else {
  14.             return new AuthorizationDecision(false);
  15.         }
  16.     }
  17. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表