HandlerInterceptor не работает с анонимным - PullRequest
2 голосов
/ 11 апреля 2019

Я пытаюсь зарегистрировать все http-запросы с помощью HandlerInterceptorAdapter:

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 (метод get) авторизованным пользователем, все работает правильно:

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 Ответ

1 голос
/ 12 апреля 2019

Я вижу, что там много чего происходит, и довольно сложно сказать наверняка без тестирования.

Но я заметил пару вещей, связанных с анонимным 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 является статическим, в вашем примере не хватает некоторого кода.То же самое для freeVideoRepository и memberRepository.

Более того, попробуйте сначала протестировать без аутентификации, чтобы убедиться, что она работает, чтобы вы могли сузить проблему.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...