【问题标题】:How to configure HttpSecurity for this situation (Spring Boot)如何针对这种情况配置 HttpSecurity(Spring Boot)
【发布时间】:2018-07-15 15:39:52
【问题描述】:

标准:

  • 未经身份验证的用户从 /oauth/token 请求令牌
  • 未经身份验证的用户也可以在 /swagger-ui.html 访问 swagger 文档
  • 应保护所有其他端点,即需要有效的令牌才能使用。

我的尝试:

SecurityConfig.java - 可能是问题的根源

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                .antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/swagger-ui.html", "/webjars/**");

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .antMatcher("/oauth/token")
                .addFilterBefore(new RESTAuthenticationTokenProcessingFilter(), BasicAuthenticationFilter.class)
                .authorizeRequests()
                .antMatchers("/**").permitAll()
                .anyRequest().authenticated();

    }
}

CustomAuthenticationProvider.java

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    final
    UserService userService;

    final
    TokenService tokenService;

    @SuppressWarnings("SpringJavaAutowiredMembersInspection")
    @Autowired
    public CustomAuthenticationProvider(TokenService tokenService, UserService userService) {
        this.tokenService = tokenService;
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        Object credentials = authentication.getCredentials();
        if (!(credentials instanceof String)) return null;
        String password = credentials.toString();
        // TODO implement hashing and salting of passwords
        UserDetails user = userService.loadUserByUsername(username);
        if (!user.getPassword().equals(password)) throw new NotAuthorisedException(AuthorisationFailureTypes.INVALID_REQUEST);

        TokenModel tokenModel = tokenService.allocateToken(user.getUsername());
        authentication.setAuthenticated(true);
        return authentication;
    }

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

RESTAuthenticationTokenProcessingFilter.java

@Component
public class RESTAuthenticationTokenProcessingFilter extends GenericFilterBean {

    public RESTAuthenticationTokenProcessingFilter() {
    }

    public RESTAuthenticationTokenProcessingFilter(UserService userService, String restUser) {
        this.userService = userService;
        this.REST_USER = restUser;
    }

    @Autowired
    private TokenService tokenService;
    private UserService userService;
    private String REST_USER;
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = getAsHttpRequest(request);
        String authToken = extractAuthTokenFromRequest(httpRequest);
        if (authToken == null) throw new NotAuthorisedException(AuthorisationFailureTypes.INVALID_REQUEST);

        String[] parts = authToken.split(" ");

        if (parts.length == 2) {
            String tokenKey = parts[1];
            if (validateTokenKey(tokenKey)) {
                TokenModel token = tokenService.getTokenById(tokenKey);
                //List<String> allowedIPs = new Gson().fromJson(token.getAllowedIP(), new TypeToken<ArrayList<String>>() {}.getType());
                //if (isAllowIP(allowedIPs, request.getRemoteAddr())) {
                if (token != null) {
                    if (token.getExpires_in() > 0) {
                        UserDetails userDetails = userService.loadUserByUsername(REST_USER);
                        TokenRequestModel authentication = new TokenRequestModel(null, null, userDetails.getUsername(), userDetails.getPassword());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                        log.info("Authenticated " + token.getAccess_token() + " via IP: " + request.getRemoteAddr());
                    } else {
                        log.info("Unable to authenticate the token: " + authToken + ". Incorrect secret or token is expired");
                        throw new NotAuthorisedException(AuthorisationFailureTypes.INVALID_REQUEST);
                    }
                    //} else {
                    //log.info("Unable to authenticate the token: " + authToken + ". IP - " + request.getRemoteAddr() + " is not allowed");l
                }
            }
        } else {
            log.info("Unable to authenticate the token: " + authToken + ". Key is broken");
            throw new NotAuthorisedException(AuthorisationFailureTypes.INVALID_REQUEST);
        }
        chain.doFilter(request, response);
    }

    private boolean validateTokenKey(String tokenKey) {
        String[] parts = tokenKey.split("-");
        return parts.length == 5;
    }

    private HttpServletRequest getAsHttpRequest(ServletRequest request) {
        if (!(request instanceof HttpServletRequest)) {
            throw new RuntimeException("Expecting an HTTP request");
        }

        return (HttpServletRequest) request;
    }


    private String extractAuthTokenFromRequest(HttpServletRequest httpRequest) {
    // Get token from header

    String authToken = httpRequest.getHeader("authorisation");

    // If token not found get it from request parameter

    if (authToken == null) {
        authToken = httpRequest.getParameter("access_token");
    }

    return authToken;
    }
}

这具有任何使用“授权”标头发出请求的用户都能够访问所有资源的结果。并且没有 Authorization 标头的用户无法请求使用令牌。

我花了很多时间尝试从其他示例中提取,并阅读 HTTPSecurity 类及其相关类中的文档,但我无法理解如何实现此配置。

任何帮助将不胜感激!

编辑 --

由于项目的性质,我不得不遵循一个涉及略微简化版本的 oauth2 的协议。不幸的是,这意味着要实现 Spring Security 中已经提供的很多功能(即我无法使用 Spring-Security-oauth2 或 Spring Security 5)。以满足我的特定需求。我非常感谢您的任何建议。

【问题讨论】:

    标签: java spring spring-boot spring-security access-token


    【解决方案1】:

    我从tutorial 找到了一个解决方案,其中涉及实施我自己的整个授权过程版本。但它有效!

    我的 SecurityConfig.java 类变成了:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    private final AuthenticationSuccessHandler loginSuccessfulHandler;
    private final AuthenticationFailureHandler loginFailureHandler;
    private final AccessDeniedHandler customAccessDeniedHandler;
    private final AuthenticationEntryPoint customAuthenticationEntryPoint;
    
    @Autowired
    public SecurityConfig(AuthenticationSuccessHandler loginSuccessfulHandler, AuthenticationFailureHandler loginFailureHandler, AccessDeniedHandler customAccessDeniedHandler, AuthenticationEntryPoint customAuthenticationEntryPoint) {
        this.loginSuccessfulHandler = loginSuccessfulHandler;
        this.loginFailureHandler = loginFailureHandler;
        this.customAccessDeniedHandler = customAccessDeniedHandler;
        this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER")
                .and()
                .withUser("admin").password("password").roles("ADMIN");
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable() // disable CSRF for this application
                .formLogin() // Using form based login instead of Basic Authentication
                .loginProcessingUrl("/oauth/token") // Endpoint which will process the authentication request. This is where we will post our credentials to authenticate
                .successHandler(loginSuccessfulHandler)
                .failureHandler(loginFailureHandler)
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/token").permitAll() // Enabling URL to be accessed by all users (even un-authenticated)
                .antMatchers("/swagger-ui.html").permitAll()
                 //.antMatchers("/secure/admin").access("hasRole('ADMIN')") // Configures specified URL to be accessed with user having role as ADMIN
                .anyRequest().authenticated() // Any resources not mentioned above needs to be authenticated
                .and()
                .exceptionHandling().accessDeniedHandler(customAccessDeniedHandler)
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                .and()
                .anonymous().disable(); // Disables anonymous authentication with anonymous role.
    }
    

    所以我必须通过实现现有接口来实现我的登录(成功/失败)处理程序等。现在我只需要实现我自己的 AuthenticationMangager 而不是当前正在使用的内存配置。

    @dur 感谢您的帮助! :)

    【讨论】:

      猜你喜欢
      • 2021-06-29
      • 2021-05-02
      • 2020-03-04
      • 2017-10-18
      • 2016-08-08
      • 2011-05-18
      • 1970-01-01
      • 2018-06-19
      • 2023-03-18
      相关资源
      最近更新 更多