首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用自定义Http报头实现基于权限的过滤的Spring安全性

使用自定义Http报头实现基于权限的过滤的Spring安全性
EN

Stack Overflow用户
提问于 2022-08-11 18:01:25
回答 1查看 272关注 0票数 0

我正在尝试使用Security来实现RBAC。用户身份验证是单独实现的,并生成sessionId供应用程序使用。我希望让Security从Http头获取sessionId,并使用sessionId从数据库中获取权限,以确定用户是否有权访问某些端点。问题是,我不知道如何根据需要从数据库中获取权限,也不知道是否正确地完成了配置。,这是我到目前为止所拥有的:

代码语言:javascript
复制
@Configuration
@EnableWebSecurity
public class CustomSecurityFilter {

  @Bean
  AuthenticationManager customAuthenticationManager(HttpHeaderAuthenticationProvider httpHeaderAuthenticationProvider) {
    return new ProviderManager(List.of(httpHeaderAuthenticationProvider));
  }

  @Bean
  HttpHeaderAuthenticationProvider newHttpHeaderAuthenticationProvider() {
    return new HttpHeaderAuthenticationProvider();
  }

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http,
      AuthenticationManager authenticationManager) throws Exception {
    http.addFilterBefore(getFilter(authenticationManager), AnonymousAuthenticationFilter.class).authorizeRequests()
        .antMatchers(HttpMethod.GET, "/api/apples").hasAuthority("viewApples")
        .antMatchers(HttpMethod.POST, "/api/apples").hasAuthority("createApples")
    return http.build();
  }

  private Filter getFilter(AuthenticationManager authenticationManager) {
    return new HttpHeaderProcessingFilter(
        new OrRequestMatcher(
            new AntPathRequestMatcher("/api/apples/**"),
        ),
        authenticationManager
    );
  }
}
代码语言:javascript
复制
public class HttpHeaderAuthenticationProvider implements AuthenticationProvider {
  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    var sessionId = ((String) authentication.getPrincipal());

    // Somehow connect to database to get session and authorities information?
    boolean isValid = sessionId != null;
    if (isValid) {
      return newPreAuthenticatedToken("sessionId", List.of());
    } else {
      throw new AccessDeniedException("Invalid sessionId");
    }
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return PreAuthenticatedAuthenticationToken.class.equals(authentication);
  }

  public static PreAuthenticatedAuthenticationToken newPreAuthenticatedToken(String userId, List<String> permissions) {
    var grantedAuthorityList = new ArrayList<GrantedAuthority>();
    for (String permission : permissions) {
      grantedAuthorityList.add(new SimpleGrantedAuthority(permission));
    }

    return new PreAuthenticatedAuthenticationToken(userId, null, grantedAuthorityList);
  }
}
代码语言:javascript
复制
public class HttpHeaderProcessingFilter extends AbstractAuthenticationProcessingFilter {

  public HttpHeaderProcessingFilter(RequestMatcher requiresAuthenticationRequestMatcher,
                                    AuthenticationManager authenticationManager) {
    super(requiresAuthenticationRequestMatcher);
    setAuthenticationManager(authenticationManager);
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException {
    return getAuthenticationManager().authenticate(
        // Not sure if we are supposed to do this
        HttpHeaderAuthenticationProvider.newPreAuthenticatedToken("sessionId", List.of())
    );
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                          Authentication authResult) throws IOException, ServletException {
    SecurityContextHolder.getContext().setAuthentication(authResult);
    chain.doFilter(request, response);
  }
}

我试着使用这些资源:

我还想知道JWT是否是一个很好的选择,可以代替使用RBAC +会话处理的自定义sessionId。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-18 22:00:27

我能够配置过滤器来使用权限。以下是我所拥有的:

代码语言:javascript
复制
@Component
@Slf4j
public class CustomPreAuthProvider implements AuthenticationProvider {

  private boolean throwExceptionWhenTokenRejected;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    if (!this.supports(authentication.getClass())) {
      return null;
    }  else {
      log.debug(String.valueOf(LogMessage.format("PreAuthenticated authentication request: %s", authentication)));
      if (authentication.getPrincipal() == null) {
        log.debug("No pre-authenticated principal found in request.");
        if (this.throwExceptionWhenTokenRejected) {
          throw new BadCredentialsException("No pre-authenticated principal found in request.");
        } else {
          return null;
        }
      } else if (authentication.getCredentials() == null) {
        log.debug("No pre-authenticated credentials found in request.");
        if (this.throwExceptionWhenTokenRejected) {
          throw new BadCredentialsException("No pre-authenticated credentials found in request.");
        } else {
          return null;
        }
      } else if (!authentication.isAuthenticated()) {
        throw new InsufficientAuthenticationException("Access token likely no longer valid.");
      }

      return authentication;
    }
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(PreAuthenticatedAuthenticationToken.class);
  }

  public void setThrowExceptionWhenTokenRejected(boolean throwExceptionWhenTokenRejected) {
    this.throwExceptionWhenTokenRejected = throwExceptionWhenTokenRejected;
  }
}
代码语言:javascript
复制
@Service
public class CustomUserDetails implements UserDetailsService {
  @Autowired
  private SessionRepository sessionRepository;

  @Autowired
  private RoleRepository roleRepository;

  @Autowired
  private AuthHelper authHelper;

  @Override
  public UserDetails loadUserByUsername(String sessionId) throws UsernameNotFoundException, IllegalStateException {
    var sessions = sessionRepository.getSession(sessionId);  // Database query for session information
    if (sessions == null || sessions.isEmpty()) {
      throw new UsernameNotFoundException("Session Not Found");
    } else if (sessions.size() > 1) {
      throw new IllegalStateException("More than one record with sessionId found");
    }

    var session = sessions.get(0);

    var authoritySet = new HashSet<String>();
    for (String role : session.getRoles()) {
      var authorities = roleRepository.getUserPrivilegesByRoleName(role);  // Database query for authorities
      for (UserRolePrivilege userRolePrivilege : authorities) {
        authoritySet.add(userRolePrivilege.getPermittedAction());
      }
    }

    var grantedAuthority = new ArrayList<GrantedAuthority>();
    for (String authority : authoritySet) {
      grantedAuthority.add(new SimpleGrantedAuthority(authority));
    }

    var introspect = authHelper.validateAccessToken(session.getSessionId(), session.getAccessToken(),
        session.getRefreshToken(), session.getExpirationTime());  // Code to verify token

    var user = new UserImpl();
    user.setUsername(session.getEmail());
    user.setPassword(session.getAccessToken());
    user.setEnabled(introspect.getIntrospect().isActive());
    user.setAccountNonExpired(introspect.getIntrospect().isActive());
    user.setAccountNonLocked(introspect.getIntrospect().isActive());
    user.setCredentialsNonExpired(introspect.getIntrospect().isActive());
    user.setAuthorities(grantedAuthority);

    return user;
  }
}
代码语言:javascript
复制
public class SessionAuthFilter extends AbstractAuthenticationProcessingFilter {
  private final CustomUserDetails customUserDetails;

  protected SessionAuthFilter(RequestMatcher requestMatcher, AuthenticationManager authenticationManager,
      CustomUserDetails customUserDetails) {
    super(requestMatcher, authenticationManager);
    this.customUserDetails = customUserDetails;
    this.setContinueChainBeforeSuccessfulAuthentication(true);
    this.setAuthenticationSuccessHandler((request, response, authentication) -> {});
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException {
    var sessionId = request.getHeader("sessionId") != null ? request.getHeader("sessionId").trim() : null;
    var user = customUserDetails.loadUserByUsername(sessionId);
    var authentication = new PreAuthenticatedAuthenticationToken(user.getUsername(), user.getPassword(),
        user.getAuthorities());
    authentication.setAuthenticated(user.isCredentialsNonExpired());
    authentication.setDetails(customUserDetails);

    SecurityContextHolder.getContext().setAuthentication(authentication);
    return this.getAuthenticationManager().authenticate(authentication);
  }
}
代码语言:javascript
复制
@Configuration
@EnableWebSecurity
public class SecurityConfig {

  @Bean
  AuthenticationManager customAuthenticationManager(CustomPreAuthProvider preAuthProvider) {
    return new ProviderManager(List.of(preAuthProvider));
  }

  @Bean
  SessionAuthFilter customAuthFilter(AuthenticationManager authManager, CustomUserDetails customUserDetails) {
    return new SessionAuthFilter(
        new OrRequestMatcher(
            new AntPathRequestMatcher("/apples/**"),
        ),
        authManager,
        customUserDetails);
  }

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http, SessionAuthFilter authFilter) throws Exception {
    http.exceptionHandling()
        .authenticationEntryPoint(new Http403ForbiddenEntryPoint())
        .accessDeniedHandler(new AccessDeniedHandlerImpl())
        .and()
        .formLogin().disable()
        .httpBasic().disable()
        .authorizeRequests()
        .antMatchers(
            "/",
            "/error",
            "/v3/api-docs/**",
            "/swagger-ui/**",
            "/swagger-ui.html",
            "/actuator/**"
        ).permitAll()
        .antMatchers(HttpMethod.GET, "/apples").hasAuthority("viewApples")
        .antMatchers(HttpMethod.POST, "/apples").hasAuthority("createApples")
        .anyRequest().authenticated()
        .and()
        .addFilterBefore(authFilter, AbstractPreAuthenticatedProcessingFilter.class)
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    return http.build();
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73325048

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档