【问题标题】:Passing UserDetails to spring controller将 UserDetails 传递给 spring 控制器
【发布时间】:2016-03-14 22:53:19
【问题描述】:

我正在研究一个使用 spring-webmvc 4.07 版和 Spring Security v 3.2 的简单 Spring Boot 项目。基本安全配置被以下配置类覆盖,以提供安全 URL 和自定义 UserDetails 实现:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private ReaderRepository readerRepository;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
        .antMatchers("/").access("hasRole('READER')")
        .antMatchers("/**").permitAll()
      .and()
      .formLogin()
        .loginPage("/login")
        .failureUrl("/login?error=true");
  }

  @Override
  protected void configure(
              AuthenticationManagerBuilder auth) throws Exception {
    auth
      .userDetailsService(new UserDetailsService() {
        @Override
        public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
          UserDetails userDetails = readerRepository.findOne(username);
          if (userDetails != null) {
            return userDetails;
          }
          throw new UsernameNotFoundException("User '" + username + "' not found.");
        }
      });
  }

}

上面的readerRepository.findOne(username)是基于接口的

public interface ReaderRepository extends JpaRepository<Reader, String> {

    List<Book> findByReader(String reader);

}

所以它正在使用登录页面上提供的用户名在数据库中寻找一个阅读器。 Reader 类是

@Entity
public class Reader implements UserDetails {

  private static final long serialVersionUID = 1L;

  @Id
  private String username;

  private String fullname;
  private String password;

  ...Setters/Getters, getAuthorities(), isAccountNonExpired(), isAccountNonLocked(), isCredentialsNonExpired(), isEnabled()

}

只有一个控制器,

@Controller
@RequestMapping("/")
@ConfigurationProperties("amazon") 
public class ReadingListController {

    private ReadingListRepository readingListRepository;
  private AmazonProperties amazonConfig;

    @Autowired
    public ReadingListController(ReadingListRepository readingListRepository,
        AmazonProperties amazonConfig) {
        this.readingListRepository = readingListRepository;
    this.amazonConfig = amazonConfig;
    }

    @RequestMapping(method=RequestMethod.GET)
    public String readersBooks(Reader reader, Model model) {
        List<Book> readingList = readingListRepository.findByReader(reader);
        if (readingList != null) {
            model.addAttribute("books", readingList);
            model.addAttribute("reader", reader);
            model.addAttribute("amazonID", amazonConfig.getAssociateId());
        }
        return "readingList";
    }

    @RequestMapping(method=RequestMethod.POST)
    public String addToReadingList(Reader reader, Book book) {
        book.setReader(reader);
        readingListRepository.save(book);
        return "redirect:/";
    }

}

我使用命令“gradle bootrun”运行应用程序。当我转到 localhost:8080/ 时,我会看到一个登录页面。当我登录时,会调用控制器的 readerBooks(...) 方法。此方法有一个 Reader 对象作为参数,其中包含登录页面上输入其用户名的已登录阅读器。 Reader 类(它实现了 UserDetails)显然是由 Spring 传入的。但是,我从未见过它以这种方式完成。我已经看到它是通过传入一个带有@AuthenticationPrincipal 注释的 Principal 或通过从控制器方法中访问 SecurityContext 来完成的,但是我找不到任何记录为什么在这种情况下传入 Reader 的任何内容。仅仅是因为 Reader实现 UserDetails?

【问题讨论】:

    标签: spring security model-view-controller controller


    【解决方案1】:

    我发现了答案。这是我在 Spring 之前从未见过的。我忽略了一门课,这是了解正在发生的事情的关键。创建了 HandlerMethodArgumentResolver 的自定义实现,以便 Spring 可以识别 Reader 类并像使用 Model 参数一样注入它:

    import org.springframework.core.MethodParameter;
    import org.springframework.security.core.Authentication;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.support.WebDataBinderFactory;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.ModelAndViewContainer;
    
    @Component
    public class ReaderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
      @Override
      public boolean supportsParameter(MethodParameter parameter) {
        return Reader.class.isAssignableFrom(parameter.getParameterType());
      }
    
      @Override
      public Object resolveArgument(MethodParameter parameter,
          ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
          WebDataBinderFactory binderFactory) throws Exception {
    
        Authentication auth = (Authentication) webRequest.getUserPrincipal();
        return auth != null && auth.getPrincipal() instanceof Reader ? auth.getPrincipal() : null;
    
      }
    
    }
    

    这个类有@Component,所以它会在WebConfig.java类中被声明为一个bean:

    import java.util.List;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    @Configuration
    public class WebConfiguration extends WebMvcConfigurerAdapter {
    
      @Override
      public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
      }
    
      @Override
      public void addArgumentResolvers(
          List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new ReaderHandlerMethodArgumentResolver());
      }
    
    }
    

    有了这段代码,Spring 现在会将 Reader 识别为控制器方法的有效参数,并注入一个填充了经过身份验证的主体的 Reader 类(如果存在)。否则,它将用 null 填充 Reader。我在 WebConfig.java 中看到了代码,但由于我不知道它的用途而忽略了它。我仍然不太确定的是为什么 HandlerMethodArgumentResolver 类的 supportsParameter 方法不是像这样简单的

    @Override
     public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(Reader.class);
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多