我正在使用spring-webmvc版本4.07和Spring Security v 3.2查看一个简单的Spring启动项目。使用以下配置类覆盖基本安全配置,以提供安全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(用户名)基于接口
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 :(得分:0)
我发现了答案。这是我以前在春天没见过的东西。我忽略了一个课程,这是正在发生的事情的关键。创建了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);
}