2016-03-14 206 views
0

我正在使用spring-webmvc 4.07版和Spring Security v 3.2查看一个简单的Spring引导项目。基本的安全配置与以下配置类,以提供安全的URL和一个自定义的UserDetails实现重写:将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(用户名),是基于在接口上

public interface ReaderRepository extends JpaRepository<Reader, String> { 

    List<Book> findByReader(String reader); 

} 

所以它正在使用登录页面上提供的用户名在数据库中寻找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?

回答

0

我发现了答案。这是我以前从未见过的东西。我忽略了一类是发生什么事情的关键。 HandlerMethodArgumentResolver的自定义实现创建,使弹簧能够识别阅读器类,并注入其作为与模型参数进行:

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的bean。 java类:

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类,如果存在的话。否则,它将使用null填充Reader。我在WebConfig.java中看到了代码,但忽略它,因为我不知道它的用途。我还不是很确定的是为什么HandlerMethodArgumentResolver类的supportsParameter方法不是简单的,像

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