如何使用Spring Boot REST限制对.html页面的访问

时间:2016-11-12 16:09:58

标签: java spring spring-security

我试图将对dashboard.html页面的访问权限限制为未经身份验证的用户。到目前为止,我没有成功。这是我的WebSecurityConfigurerAdapter

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private CustomAuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/index.html", "/",
                         "/login.html","/signup.html", "/videos/**",
                         "/login", "/logout", "/images/**", "/fonts/**",
                         "/css/**", "/js/**", "/pages/**", "/sass/**"
                         ).permitAll()
                .and()
            .authorizeRequests()
                .antMatchers("/dashboard/**", "/dashboard.html/**").authenticated()         
                .and()
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .addFilterBefore(new StatelessLoginFilter("/login", tokenAuthenticationService, userDetailsService, authenticationManager()), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class)
            .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .usernameParameter("email")
                .passwordParameter("password")
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .and()
            .logout()
                .logoutSuccessUrl("/")
                .deleteCookies("JSESSIONID")
                .permitAll()
                .and()
            .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .and()
            .addFilterAfter(new CsrfTokenFilter(), CsrfFilter.class);
    }
}

每当我设置这种方式时,每当我尝试登录时,都会导致无限重定向循环。浏览器尝试导航到dashboard.html但受限制。这会导致重定向到登录页面,登录页面会尝试重定向到仪表板,因为有一个有效的令牌。

如果我将其设置如下,则每个人都可以访问dashboard.html并拨打/dashboard端点,这是不需要的:

            http
                .authorizeRequests()
                     .antMatchers("/index.html", "/",
                             "/login.html","/signup.html", "/videos/**",
                             "/login", "/logout", "/images/**", "/fonts/**",
                             "/css/**", "/js/**", "/pages/**", "/sass/**",
                             "/dashboard/**", "/dashboard.html/**").permitAll()
                     .and()
                .authorizeRequests()
                     .anyRequest().authenticated()   

我的登录使用JWT令牌并使用下面的过滤器设置SecurityContext占位符:

class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {

    private final TokenAuthenticationService tokenAuthenticationService;

    private final CustomUserDetailsService userDetailsService;

    protected StatelessLoginFilter(String urlMapping, TokenAuthenticationService tokenAuthenticationService,
            CustomUserDetailsService userDetailsService, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(urlMapping));
        this.userDetailsService = userDetailsService;
        this.tokenAuthenticationService = tokenAuthenticationService;
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, IOException, ServletException {
        final BusinessUser user = new ObjectMapper().readValue(request.getInputStream(), BusinessUser.class);
        final UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken(
                user.getEmail(), user.getPassword());
        return getAuthenticationManager().authenticate(loginToken);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain, Authentication authentication) throws IOException, ServletException {

        final BusinessUser authenticatedUser = userDetailsService.loadUserByUsername(authentication.getName());
        final UserAuthentication userAuthentication = new UserAuthentication(authenticatedUser);

        tokenAuthenticationService.addAuthentication(response, userAuthentication);

        SecurityContextHolder.getContext().setAuthentication(userAuthentication);
    }

我使用行SecurityContextHolder.getContext().setAuthentication(userAuthentication);来设置身份验证。这完全没问题。如果在匹配从用户发送的凭证的DB中找到用户,则安全上下文可用于检索与用户相关联的各种数据。

我的问题:如何限制页面dashboard.html并将/dashboard端点调用给未经身份验证的用户(SecurityContextHolder内没有身份验证对象的用户)?

1 个答案:

答案 0 :(得分:3)

您可以将自定义RequestMatcherdenyAll结合使用。首先,您的自定义匹配器:

public class PermittedPagesMatcher implements RequestMatcher {

    @Override
    public boolean matches(HttpServletRequest httpServletRequest) {
        if (matchesToPaths(httpServletRequest,"/index.html", "/", "/login.html","/signup.html", "/videos/**", "/login", "/logout", "/images/**", "/fonts/**", "/css/**", "/js/**", "/pages/**", "/sass/**", "/dashboard/**", "/dashboard.html/**")) {
            return true;
        }

        if (matchesToPaths(httpServletRequest, "/dashboard/**", "/dashboard.html/**")) {
            return httpServletRequest.getUserPrincipal() == null;
        }

        return false;
    }

    private boolean matchesToPaths(HttpServletRequest httpServletRequest, String... paths) {
        for (String p : paths) {
            if (new AntPathRequestMatcher(p).matches(httpServletRequest)) {
                return true;
            }
        }
        return false;
    }
}

此自定义RequestMatcher会将您的请求过滤到所有默认页面的允许页面,并且仅当请求未经过身份验证时才可使用仪表板。

其次,结合匹配器和denyAll()

 http
            .authorizeRequests()
            .requestMatchers(new PermittedPagesMatcher())
            .permitAll()
        .and()
            .antMatchers("/dashboard/**", "/dashboard.html/**")
            .denyAll()
        .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated()  

denyAll()确保默认情况下不允许任何人访问此页面。

注意:许可和拒绝的顺序很重要!