HandlerInterceptor不能与匿名一起使用

时间:2019-04-11 20:42:02

标签: java spring spring-boot spring-mvc

我尝试通过HandlerInterceptorAdapter记录所有http请求:

public class LogRequestInterceptor extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("afterConcurrentHandlingStarted: {}", request.getRequestURI());
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        log.debug("postHandle: {}", request.getRequestURI());
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        log.debug("afterCompletion: {}", request.getRequestURI());
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("afterConcurrentHandlingStarted: {}", request.getRequestURI());
    }
}

当我由授权用户请求/ api / videos / free和/ api / members(方法获取)时,一切正常:

2019-04-11 23:21:35.372 DEBUG PopovDesktopLinux --- [nio-8080-exec-2] c.h.v.b.m.LogRequestInterceptor          : afterConcurrentHandlingStarted: /api/videos/free
2019-04-11 23:21:35.611 DEBUG PopovDesktopLinux --- [nio-8080-exec-2] c.h.v.b.m.LogRequestInterceptor          : postHandle: /api/videos/free
2019-04-11 23:21:35.611 DEBUG PopovDesktopLinux --- [nio-8080-exec-2] c.h.v.b.m.LogRequestInterceptor          : afterCompletion: /api/videos/free
2019-04-11 23:21:37.167 DEBUG PopovDesktopLinux --- [nio-8080-exec-1] c.h.v.b.m.LogRequestInterceptor          : afterConcurrentHandlingStarted: /api/members
2019-04-11 23:21:37.189 DEBUG PopovDesktopLinux --- [nio-8080-exec-1] c.h.v.b.m.LogRequestInterceptor          : postHandle: /api/members
2019-04-11 23:21:37.189 DEBUG PopovDesktopLinux --- [nio-8080-exec-1] c.h.v.b.m.LogRequestInterceptor          : afterCompletion: /api/members

如果我通过匿名提出相同的请求,则日志看起来如下:

2019-04-11 23:22:05.813 DEBUG PopovDesktopLinux --- [nio-8080-exec-3] c.h.v.b.m.LogRequestInterceptor          : afterConcurrentHandlingStarted: /api/videos/free
2019-04-11 23:22:05.820 DEBUG PopovDesktopLinux --- [nio-8080-exec-3] c.h.v.b.m.LogRequestInterceptor          : afterCompletion: /api/videos/free

/ api / videos / free和/ api / members非常相似:

@Log4j2
@RestController
@RequestMapping("/api/members")
public class MemberController {
    @PreAuthorize("hasRole('ADMIN')")
    @Transactional
    @RequestMapping(method = RequestMethod.GET)
    public List<com.helan.videoafisha.dto.general.Member> list() {
        return modelMapper.map(
                memberRepository.findAll(),
                new TypeToken<List<com.helan.videoafisha.dto.general.Member>>(){}.getType()
        );
    }
}

@Log4j2
@RestController
@RequestMapping("/api/videos")
public class VideoController {
    @PreAuthorize("hasAnyRole('DEVICE', 'ADMIN')")
    @Transactional
    @RequestMapping(value = "/free", method = RequestMethod.GET)
    public List<com.helan.videoafisha.dto.backend.frontend.admin.videos.free.FreeVideo> freeVideos() {
        return modelMapper.map(
                freeVideoRepository.findAllWithVideo(),
                new TypeToken<List<com.helan.videoafisha.dto.backend.frontend.admin.videos.free.FreeVideo>>(){}.getType()
        );
    }
}

负责身份验证的代码:

@Log4j2
@Service
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private TokenUtil tokenUtil;

    @Autowired
    private UserDetailsService memberUserDetailsService;

    @Autowired
    private UserDetailsService deviceUserDetailsService;

    @Value("${mvc.header.token}")
    private String tokenHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        final String tokenString = httpServletRequest.getHeader(tokenHeader);

        try {
            if (tokenString != null && !tokenString.equals("")) {
                Token token = tokenUtil.parseToken(tokenString);
                UserDetails userDetails = null;
                switch (token.getAuthenticateTarget()) {
                    case Member: userDetails = memberUserDetailsService.loadUserByUsername(token.getUsername()); break;
                    case Device: userDetails = deviceUserDetailsService.loadUserByUsername(token.getUsername()); break;
                    default: log.error(String.format("Unknown authentication target: %s", token.getAuthenticateTarget().toString()));
                }

                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(token.getUsername(), null, userDetails.getAuthorities());
                authenticationToken.setDetails(userDetails);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        } catch (TokenException | UsernameNotFoundException e) {
            log.debug(String.format("%s, %s", httpServletRequest.getRequestURI(), e.getLocalizedMessage()));
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

添加拦截器:

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logRequestInterceptor).addPathPatterns("/api/**");
    }
}

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    TokenAuthenticationFilter tokenAuthenticationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .exceptionHandling().authenticationEntryPoint(http403ForbiddenEntryPoint()).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/api/devices/authenticate").permitAll()
                .antMatchers("/api/members/authenticate").permitAll()
                .antMatchers("/api/*").authenticated().and()
                .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .headers().frameOptions().sameOrigin().cacheControl();
    }

    @Bean
    protected PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(11);
    }

    @Bean
    protected Http403ForbiddenEntryPoint http403ForbiddenEntryPoint() {
        return new Http403ForbiddenEntryPoint();
    }
}

为什么/ api / videos / free的拦截器部分调用方法,而/ api / members的拦截器根本没有引起作用?

1 个答案:

答案 0 :(得分:1)

我看到那里发生了很多事情,如果不经过测试,很难确定。

但是我发现了与您提到的匿名TypeToken相关的几件事。

我认为可以将其提取为常量,如下所示:

@Log4j2
@RestController
@RequestMapping("/api/videos")
public class VideoController {

    private static final TypeToken<List<com.helan.videoafisha.dto.backend.frontend.admin.videos.free.FreeVideo>> FREE_VIDEO_TYPE = new TypeToken<>(){}.getType();

    @PreAuthorize("hasAnyRole('DEVICE', 'ADMIN')")
    @Transactional
    @RequestMapping(value = "/free", method = RequestMethod.GET)
    public List<com.helan.videoafisha.dto.backend.frontend.admin.videos.free.FreeVideo> freeVideos() {
        return modelMapper.map(FREE_VIDEO_TYPE, freeVideoRepository.findAllWithVideo());
    }
}

通过这样做,您还避免了在每次调用中仅为了获取其类型而创建一个新对象。

另一件事是,看起来像添加令牌,并且映射中的查询值被反转,因此在我的示例中,我将映射添加为键,然后添加了查询的值。

我也看不到modelMapper对象来自任何地方。除非那是一个类并且其map方法是静态的,否则您的示例将丢失一些代码。 freeVideoRepositorymemberRepository都是相同的。

此外,请先尝试在不进行身份验证的情况下进行测试,以查看其是否有效,以便您缩小问题范围。

我希望它会有所帮助。