2016-03-14 205 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); 
}