【问题标题】:Spring Security 3 - always return error 302Spring Security 3 - 总是返回错误 302
【发布时间】:2014-10-11 00:35:50
【问题描述】:

我使用 Spring 4 创建一个简单的应用程序。最近,我将 Spring Security 3 添加到项目中,但总是得到错误代码 302(所以它总是重定向到 home 页面)。

这是我的SecurityConfig

@Configuration
@EnableWebMvcSecurity
@ComponentScan(basePackages = { "com.moon.repository" })
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("hello").password("world").roles("USER");
}

@Override
public void configure(WebSecurity web) throws Exception {
    web
    .ignoring().antMatchers("/resources/**", "/views/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
            .antMatchers("/","/home").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/home")
            .loginProcessingUrl("/acct/signin")
            .and()
            .logout()
            .permitAll();
}

}

我有一个名为 AccountController 的控制器:

@Controller
@RequestMapping(value = "/acct")
public class AccountController {

private final Logger logger = LoggerFactory.getLogger(AccountController.class);

@RequestMapping(value = "/signin", method = RequestMethod.POST)
public String signin(@RequestParam("username") String username,
        @RequestParam("password") String password) {

    logger.info("======== [username:{0}][password:{1}] ========", username, password);

    if ("error@1.1".equalsIgnoreCase(username)) {
        return "error";
    } else {
        return "demo";
    }
}

}

我的 WEB-INF 结构:

WEB-INF
----views
--------home.jsp
--------demo.jsp
--------error.jsp

流程是这样的:

  1. 用户使用http://mylocal:8080/moon 访问该网站 => 它显示 home.jsp
  2. 用户按下按钮 SignIn 并弹出一个要求输入用户名和密码的子窗口 => 仍在 home.jsp
  3. 用户按下提交按钮 => 我认为它会转到 /acct/signin 并返回到 /demo,但我在 Google Chrome 中看到错误 302,然后它又转到 /home

有什么想法吗?我被困了整整两天,现在我几乎绝望了......

非常感谢大家看我的问题

==================================== 第一次更新======= =============================

更新:home.jsp

中的表单
<form:form role="form" method="POST" action="acct/signin"
class="form-signin">
<div class="row">
    <div class="col-lg-5">
        <input name="username" size="20" type="email"
            class="form-control" placeholder="Email address" required
            autofocus> 
            <input name="password" type="password"
                    class="form-control" placeholder="Password" required>
                <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </div>
</div>
</form:form>

===================================== 第二次更新======= =============================

我尝试实现 UserDetailsS​​ervice(不使用内存身份验证),但仍然...同样的问题 - 错误 302

AppUserDetailsS​​erviceImpl.java

@Component
public class AppUserDetailsServiceImpl implements UserDetailsService {

    private final Logger logger = LoggerFactory.getLogger(AppUserDetailsServiceImpl.class);

    @Override
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

        logger.info("loadUserByUsername username=" + username);
        logger.info("======== {} ========",SecurityContextHolder.getContext().getAuthentication());

        if (!username.equals("hello")) {
            throw new UsernameNotFoundException(username + " not found");
        }

        // creating dummy user details
        return new UserDetails() {

            private static final long serialVersionUID = 2059202961588104658L;

            @Override
            public boolean isEnabled() {
                return true;
            }

            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            @Override
            public String getUsername() {
                return username;
            }

            @Override
            public String getPassword() {
                return "world";
            }

            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                List<SimpleGrantedAuthority> auths = new java.util.ArrayList<SimpleGrantedAuthority>();
                auths.add(new SimpleGrantedAuthority("USER"));
                return auths;
            }
        };
    }

日志显示:

[14/08/19 15:16:32:200][INFO ][com.moon.repository.AppUserDetailsServiceImpl][loadUserByUsername](24) loadUserByUsername username=hello
[14/08/19 15:16:32:200][INFO ][com.moon.repository.AppUserDetailsServiceImpl][loadUserByUsername](25) ======== org.springframework.security.authentication.UsernamePasswordAuthenticationToken@f1e4f742: Principal: com.moon.repository.AppUserDetailsServiceImpl$1@e3dc1b1; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@12afc: RemoteIpAddress: 127.0.0.1; SessionId: 023BC9A8B997ECBD826DD7C33AF55FC7; Granted Authorities: USER ========

【问题讨论】:

  • 只是为了断言我的答案,您可以在您的登录方法中的任何位置设置一个调试点并评估以下表达式:SecurityContextHolder.getContext().getAuthentication() 并发布结果吗? (尤其是是校长)
  • 嗨@m4rtin,我在代码中使用Log 进行调试,所以我在AccountController(“/signin”)的开头创建了一个。请参阅edit。奇怪的是,我在日志文件中找不到这个日志……看起来它(“acct/signin”)没有被触发。
  • 为确保调用了处理登录过程的方法,您可以在其中放置一个调试点并在调试模式下启动您的应用程序。但是在我希望您在我的其他评论中尝试的解决方案中,您将无法这样做,因为 Spring Security 将负责处理登录请求(您仍然可以在 UserDetailsS​​ervice 中放置一个调试点)。

标签: java spring spring-mvc spring-security


【解决方案1】:

我相信 Spring 会将您重定向到 /home,因为您实际上并未通过登录过程对用户进行身份验证。

  1. 您通过 http://mylocal:8080/moon 返回 home.jsp 视图访问您的网络应用程序
  2. 单击登录按钮,提交登录表单由于没有明确声明表单登录,Spring Security 将显示用户名和密码提示框,供最终用户输入其凭据
  3. 然后这些凭据被 POST 到登录处理 URL (/acct/signin),您恰好在 AccountController 中使用 signin 方法映射到该 URL
  4. 此类控制器无法通过 Spring 方式对用户进行身份验证,但仍通过返回字符串将请求重定向到 /demo
  5. /demo 路径被保护(.anyRequest().authenticated())任何未经身份验证的用户,由于当前用户确实未经身份验证,Spring Security 会自动将请求重定向到登录页面
  6. 你最终在/home (.loginPage("/home"))

使用InMemoryUserDetailsManagerConfigurer(参见inMemoryAuthentication javadoc),您只能通过配置的凭据成功登录。如果你想要一个成熟的身份验证系统,你必须为你的 Spring Security 配置提供一个UserDetailsService 实现(通过userDetailsService 方法)。


编辑:在与 chialin.lin 的对话之后,似乎缺少的配置是 defaultSuccessfulUrl,以便 Spring Security 知道一旦通过身份验证将用户重定向到哪里。

【讨论】:

  • 嗨@m4rtin,很抱歉我忘记放home.jsp 信息。基本上,我将登录表单放在 home.jsp 中。请看我的更新edit
  • 哦,好的,所以您确实提供了输入字段来输入凭据,我会相应地更新我的答案。不过,我相信我的其余答案是正确的。
  • 嗨@m4rtin,我实现了 UserDetailsS​​ervice(named AppUserDetailsS​​erviceImpl) 但问题仍然存在。我已经在帖子中更新了
  • 好吧,看来事情肯定不会按照我在回答中写的内容进行,因为您确实已通过身份验证,因此您继续导航应该没有问题。也许是映射问题?例如,您可以尝试删除您的 loginProcessingUrl 配置并将 defaultSuccessUrl 添加到 /demo 吗?
  • @chialin.lin 确切地说:Spring Security 将使用 Spring Security 过滤器链中自己的过滤器之一(通常是 UsernamePasswordAuthenticationFilter)来处理用户身份验证,这样您就不必自己动手了。我认为 loginProcessingUrl 是为了绕过这样的过程,让 dev 处理自己的身份验证过程,但显然我错了。无论如何,我很高兴你找到了解决问题的方法,我会编辑我的答案。快乐编码:)
【解决方案2】:

为避免创建新的琐碎的SuccessHandler,请在过滤器中覆盖successfulAuthentication 方法,并在安全上下文中设置Authentication 对象后调用chain.doFilter() 方法。

【讨论】:

    【解决方案3】:

    对我来说,我来自一个稍微不同的用例,但“突然”在它完美工作之前遇到了同样的问题。
    我的设置 Spring 带有 ExtJs 前端,我现在在其中构建了一个休息界面。
    这一切都非常好,然后突然我开始收到 http status 302 响应(WTH?)

    因为我是按照这个例子通过代码实现的:https://octoperf.com/blog/2018/03/08/securing-rest-api-spring-security/
    有一个 SimpleUrlAuthenticationSuccessHandler 的声明。
    请参阅 4.4 SecurityConfig,其中 TokenAuthenticationFilter 是使用类 NoRedirectStrategy 构造的;参见4.1 重定向策略

    反过来,如果在我的 AbstractAuthenticationProcessingFilter 扩展中没有设置此 NoRedirectStrategy,它会向我显示 http 302 响应。

    【讨论】:

      猜你喜欢
      • 2018-10-13
      • 2019-01-07
      • 2012-07-29
      • 2018-08-15
      • 2019-05-24
      • 2016-01-10
      • 2015-11-12
      • 2015-10-23
      • 2018-08-28
      相关资源
      最近更新 更多