Java Spring Security 3.2.1具有不同WebSecurity配置适配器的多个登录表单
我将SpringSecurity3.2.1.RELEASE与SpringMVC4.0.4.RELEASE一起使用 我正在尝试为一个web应用程序设置Spring安全性,该应用程序将有两个不同的登录条目页面。我需要的网页是不同的,因为他们将被样式和访问不同 第一个登录页面用于管理员用户,并保护管理员页面/Admin/** 第二个登录页面用于客户用户,并保护客户页面/Customer/** 我试图设置WebSecurity ConfigureAdapter的两个子类来配置各个HttpSecurity对象 CustomPerformLoginWebSecurity正在保护客户页面,并在未经授权的情况下重定向到客户登录页面。 AdminFormLoginWebSecurity在未经授权的情况下保护重定向到管理员登录页面的管理员页面 不幸的是,似乎只有第一个配置被强制执行。我想我错过了一些额外的东西,使这两个工作Java Spring Security 3.2.1具有不同WebSecurity配置适配器的多个登录表单,java,spring,spring-mvc,spring-security,Java,Spring,Spring Mvc,Spring Security,我将SpringSecurity3.2.1.RELEASE与SpringMVC4.0.4.RELEASE一起使用 我正在尝试为一个web应用程序设置Spring安全性,该应用程序将有两个不同的登录条目页面。我需要的网页是不同的,因为他们将被样式和访问不同 第一个登录页面用于管理员用户,并保护管理员页面/Admin/** 第二个登录页面用于客户用户,并保护客户页面/Customer/** 我试图设置WebSecurity ConfigureAdapter的两个子类来配置各个HttpSecurity
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("customer").password("password").roles("CUSTOMER").and()
.withUser("admin").password("password").roles("ADMIN");
}
@Configuration
@Order(1)
public static class CustomerFormLoginWebSecurity extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/", "/signin/**", "/error/**", "/templates/**", "/resources/**", "/webjars/**");
}
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/customer/**").hasRole("CUSTOMER")
.and()
.formLogin()
.loginPage("/customer_signin")
.failureUrl("/customer_signin?error=1")
.defaultSuccessUrl("/customer/home")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.and()
.logout()
.permitAll();
http.exceptionHandling().accessDeniedPage("/customer_signin");
}
}
@Configuration
public static class AdminFormLoginWebSecurity extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/", "/signin/**", "/error/**", "/templates/**", "/resources/**", "/webjars/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/admin_signin")
.failureUrl("/admin_signin?error=1")
.defaultSuccessUrl("/admin/home")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.and()
.logout()
.permitAll();
http.exceptionHandling().accessDeniedPage("/admin_signin");
}
}
}
也许这篇文章可以帮助你: 这是一个不同版本的spring security,但问题是相同的:只采用第一个配置 通过更改两个登录页面之一的登录处理url,似乎已经解决了这个问题,但人们建议使用相同的url处理,但使用ViewResolver使用不同的布局。如果您使用相同的机制对用户进行身份验证(身份验证机制负责处理浏览器发送的凭据),则这是一种解决方案 这篇文章似乎还说,如果您更改了loginProcessingUrl,您将成功:
spring登录链中重定向到登录页面的组件是身份验证过滤器,使用
http.formLogin()
时插入的过滤器是DefaultLoginPageGeneratingFilter
如果未提供登录页面url,此筛选器将重定向到登录url或生成默认的基本登录页面
然后,您需要的是一个定制的身份验证过滤器,它具有定义需要哪个登录页面的逻辑,然后将其插入到spring安全链中以代替单页面身份验证过滤器
考虑创建一个TwoPageLoginAuthenticationFilter
,方法是子类化DefaultLoginPageGeneratingFilter
并重写getLoginPageUrl()
,如果这还不够,则复制代码并修改它以满足您的需要
此筛选器是一个GenericFilterBean
,因此您可以这样声明它:
@Bean
public Filter twoPageLoginAuthenticationFilter() {
return new TwoPageLoginAuthenticationFilter();
}
然后尝试只构建一个http配置,不要设置formLogin()
,而是:
http.addFilterBefore(twoPageLoginAuthenticationFilter, ConcurrentSessionFilter.class);
这将在链中正确的位置插入两种形式的身份验证过滤器。我为多个登录页面提供的解决方案涉及一个http身份验证,但我提供了自己的实现
AuthenticationEntryPoint
AuthenticationFailureHandler
LogoutSuccessHandler
当我成功通过客户身份验证并希望注销时,LogoutSuccessHandler应将我带回CUSTOMER_signin页面 我有一个类似的要求,管理员需要在管理员登录页面进行身份验证,才能访问url中带有管理员令牌的页面 首先,我定义了一个类,该类允许我获取令牌列表(每种类型的登录页面对应一个令牌) PathTokenNotFoundException扩展AuthenticationException,以便您可以用常规方式处理它
public class PathTokenNotFoundException extends AuthenticationException {
public PathTokenNotFoundException(String msg) {
super(msg);
}
public PathTokenNotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
接下来,我提供了一个AuthenticationFailureHandler
的实现,它查看请求头中的referer url,以确定用户要指向哪个登录错误页面
@Component
public class PathUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private final PathTokens tokens;
@Autowired
public PathUrlAuthenticationFailureHandler(PathTokens tokens) {
super();
this.tokens = tokens;
}
/**
* Performs the redirect or forward to the {@code defaultFailureUrl associated with this path} if set, otherwise returns a 401 error code.
* <p/>
* If redirecting or forwarding, {@code saveException} will be called to cache the exception for use in
* the target view.
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
setDefaultFailureUrl(getFailureUrlFromPath(request));
super.onAuthenticationFailure(request, response, exception);
}
private String getFailureUrlFromPath(HttpServletRequest request) {
String refererUrl = request.getHeader("Referer");
if (tokens.isTokenInPath(refererUrl)) {
return "/" + tokens.getTokenFromPath(refererUrl) + "_signin?error=1";
}
throw new PathTokenNotFoundException("Token not found in referer URL " + refererUrl + " when retrieving failureUrl for login form");
}
}
最后一步是在安全配置中将它们连接在一起
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired PathLoginAuthenticationEntryPoint loginEntryPoint;
@Autowired PathUrlAuthenticationFailureHandler loginFailureHandler;
@Autowired
PathUrlLogoutSuccessHandler logoutSuccessHandler;
@Bean
public PathTokens pathTokens(){
return new PathTokens(Arrays.asList("customer", "admin"));
}
@Autowired
public void registerGlobalAuthentication(
AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("customer").password("password").roles("CUSTOMER").and()
.withUser("admin").password("password").roles("ADMIN");
}
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/", "/signin/**", "/error/**", "/templates/**", "/resources/**", "/webjars/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http .csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("CUSTOMER")
.and()
.formLogin()
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("j_username").passwordParameter("j_password")
.failureHandler(loginFailureHandler);
http.logout().logoutSuccessHandler(logoutSuccessHandler);
http.exceptionHandling().authenticationEntryPoint(loginEntryPoint);
http.exceptionHandling().accessDeniedPage("/accessDenied");
}
}
一旦你配置好了,你需要一个控制器来引导到实际登录页面。下面的Signincontroller检查queryString中是否有表示登录错误的值,然后设置用于控制错误消息的属性
@Controller
@SessionAttributes("userRoles")
public class SigninController {
@RequestMapping(value = "customer_signin", method = RequestMethod.GET)
public String customerSignin(Model model, HttpServletRequest request) {
Set<String> userRoles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("userRole", userRoles);
if(request.getQueryString() != null){
model.addAttribute("error", "1");
}
return "signin/customer_signin";
}
@RequestMapping(value = "admin_signin", method = RequestMethod.GET)
public String adminSignin(Model model, HttpServletRequest request) {
Set<String> userRoles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("userRole", userRoles);
if(request.getQueryString() != null){
model.addAttribute("error", "1");
}
return "signin/admin_signin";
}
}
@控制器
@SessionAttributes(“用户角色”)
公共类签名控制器{
@RequestMapping(value=“customer\u signin”,method=RequestMethod.GET)
公共字符串customerSignin(模型,HttpServletRequest){
设置userRoles=AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().GetAuthories());
model.addAttribute(“userRole”,userRoles);
if(request.getQueryString()!=null){
model.addAttribute(“错误”,“1”);
}
返回“签到/客户签到”;
}
@RequestMapping(value=“admin\u signin”,method=RequestMethod.GET)
公共字符串adminSignin(模型,HttpServletRequest){
设置userRoles=AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().GetAuthories());
model.addAttribute(“userRole”,userRoles);
if(request.getQueryString()!=null){
model.addAttribute(“错误”,“1”);
}
返回“登录/管理员登录”;
}
}
我也遇到了这个问题,发现我错过了第一个过滤部分
这个:
http.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
应该是:
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
添加第一个筛选。antMatcher(“/admin/**”)将首先对其进行筛选,以便它将使用AdminFormLoginWebSecurity代替
@Controller
@SessionAttributes("userRoles")
public class SigninController {
@RequestMapping(value = "customer_signin", method = RequestMethod.GET)
public String customerSignin(Model model, HttpServletRequest request) {
Set<String> userRoles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("userRole", userRoles);
if(request.getQueryString() != null){
model.addAttribute("error", "1");
}
return "signin/customer_signin";
}
@RequestMapping(value = "admin_signin", method = RequestMethod.GET)
public String adminSignin(Model model, HttpServletRequest request) {
Set<String> userRoles = AuthorityUtils.authorityListToSet(SecurityContextHolder.getContext().getAuthentication().getAuthorities());
model.addAttribute("userRole", userRoles);
if(request.getQueryString() != null){
model.addAttribute("error", "1");
}
return "signin/admin_signin";
}
}
http.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
http.csrf().disable()
.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")