首页 > 解决方案 > spring boot 安全配置不正确

问题描述

我是新手Spring bootSpring boot security。但是,使用现有的示例代码,我编写了一个可以正常工作的代码。在我的设计中,我有一个单独的登录页面,名为login.html. 现在,我改变了我的设计,我的登录和注册form在主页上是一个单一的,这似乎是有问题的。在这里,我更详细地解释了这个问题:

  1. 我打开主页,然后单击登录按钮。2)我输入用户名和密码,然后单击登录按钮。然后,我看到了dashboard预期的页面。到现在为止还挺好。

主页如下所示: 在此处输入图像描述 这是登录/注册表单的代码home.html

<form th:action="@{/signup}" method="post">
               <input type="hidden" name="action" id="action" value="">
            
                    <div>
                        <input type="email" name="email" id="inputEmail" placeholder="email" required>
                    </div>
                    <div>
                        
                        <input type="password" name="password" id="inputPassword" placeholder="password" required>
                    </div>
                <button type="submit">SIGN IN</button>
            </form>

这是signin/signup控制器:

 @RequestMapping(value = "/signup", method = RequestMethod.POST)
public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult, @RequestParam("action") String action) {
    
    String act = action;
    
    ModelAndView modelAndView = new ModelAndView();
    User userExists = userService.findUserByEmail(user.getEmail());

    if(action.equalsIgnoreCase("signup")) {
            if (userExists != null) {
        bindingResult
                .rejectValue("email", "error.user",
                 "There is already a user registered with the username");
    }
    if (bindingResult.hasErrors()) {
        modelAndView.setViewName("signup");
    } else {
        userService.saveUser(user);
        modelAndView.addObject("successMessage", "User has been registered successfully");
        modelAndView.addObject("user", new User());
        modelAndView.setViewName("home");
    }
    }else if(action.equalsIgnoreCase("signin")) {
         modelAndView.addObject("currentUser", userExists);
         modelAndView.addObject("firstname", userExists.getFirstname());
         modelAndView.addObject("lastname", userExists.getLastname());

         modelAndView.addObject("adminMessage", "Content Available Only for Users with Admin Role");
        
        modelAndView.setViewName("dashboard");
    }
return modelAndView;

}

到目前为止,一切正常,登录后,我被转发到如下所示的dashboard页面: 在此处输入图像描述 现在,如果我单击John哪个应该将我转发到个人资料页面或单击submit哪个应该将我转发到另一个页面,它返回到用户未登录的主页。这是仪表板中的表单,当我单击submit按钮时应该调用控制器方法:

    <form  action="/addReview" method="POST" name="addReviewForm"onsubmit="return validateForm()">
<input type="hidden" id="category"name="category" />
<input type="text" id="first_input" name="first_point" placeholder="Point 1">
<input type="text" id="seventh_input" name="seventh_point" placeholder="Point 7">
<button type="submit">submit</button></form>

这是安全性的配置:

  @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/").permitAll()
           // .antMatchers("/login").permitAll()
            .antMatchers("/signup").permitAll()
            .antMatchers("/search").permitAll()
            .antMatchers("/dashboard/**").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
            .loginPage("/").failureUrl("/?error=true")
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling();
}

我认为问题在于.loginPage("/").failureUrl("/?error=true"),但我不确定。我没有任何单独的登录页面。同时,该函数validateForm()被正确调用。只是没有调用控制器方法。

我希望有人能帮助我。

提前致谢,

##更新 1: ##

我更改并简化了代码,以便更容易找到问题。现在,我的表格中有以下表格home.html

    <form th:action="@{/}" method="post">
    <input type="email" name="email" id="inputEmail" placeholder="email" required>
    <input type="password" name="password" id="inputPassword" placeholder="password" required>
<input type="submit" value="SIGN IN" />

以及以下配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
    
  http
  .authorizeRequests()
  .antMatchers("/").permitAll()
  .antMatchers("/search").permitAll()
  .antMatchers("/dashboard/**").hasAuthority("ADMIN").anyRequest()
  .authenticated()
  .and().csrf().disable().formLogin().successHandler(customizeAuthenticationSuccessHandler)
  .loginPage("/")
  .failureUrl("/?error=true")
  .usernameParameter("email")
  .passwordParameter("password").and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling();
}
    

以及以下控制器:

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public ModelAndView createNewUser(@Valid User user, BindingResult bindingResult, @RequestParam("email") String email) {

ModelAndView modelAndView = new ModelAndView();
        User userExists = userService.findUserByEmail(user.getEmail());
             modelAndView.addObject("currentUser", userExists);
             modelAndView.addObject("firstname", userExists.getFirstname());
             modelAndView.addObject("lastname", userExists.getLastname());
          
            modelAndView.setViewName("dashboard");
    return modelAndView;

    }
     @RequestMapping(value = {"/","/home"}, method = RequestMethod.GET)
    public ModelAndView home() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("home");
        return modelAndView;
    }

使用此代码,我什至看不到仪表板。单击sign in按钮将我返回到该http://localhost:8080/?error=true页面。

标签: javahtmlspringspring-mvcspring-security

解决方案


从安全配置开始,我将使用 lambda DSL 重写它并解释它在做什么。

protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests(authorize -> authorize
                    .antMatchers("/").permitAll()
                    .antMatchers("/signup").permitAll()
                    .antMatchers("/search").permitAll()
                    .antMatchers("/dashboard/**").hasAuthority("ADMIN")
                    .anyRequest().authenticated()
            )
            .formLogin(formLogin -> formLogin
                    .successHandler(customizeAuthenticationSuccessHandler)
                    .loginPage("/")
                    .failureUrl("/?error=true")
                    .usernameParameter("email")
                    .passwordParameter("password")
            )
            // ...
}

这就是说允许对“/”、“/signup”、“/search”的
请求,而无需用户登录。对“/dashboard/**”的请求需要用户登录并具有“角色”行政”。
对任何其他端点的请求都需要用户登录。
这意味着对“/addReview”的请求需要用户登录。

在您描述的情况下,用户未登录,这就是不允许他们访问“/addReview”的原因。
Spring Security 阻止请求到达控制器,因为没有登录用户。

Spring Security 将谁通过身份验证的详细信息存储在SecurityContextHolder.
查看提供的代码,SecurityContextHolder从未填充过,因此从 Spring Security 的角度来看,没有人登录。

上面的“/signup”端点只是在视图(HTML 页面)上设置一些字段,而不是实际执行登录。

由于您已.formLogin()在配置中启用,因此 Spring Security 提供了大部分登录功能。
您只需将用户名(在本例中为电子邮件)和密码发布到生成的“/”登录端点,框架将验证用户并填充SecurityContextHolder.

<form th:action="@{/}" method="post">
    <div>
        <input type="email" name="email" id="inputEmail" placeholder="email" required>
    </div>
    <div>
        <input type="password" name="password" id="inputPassword" placeholder="password" required>
    </div>
    <input type="submit" value="SIGN IN" />
</form>

然后,您可以仅将“/signup”端点用于注册,而不是将其与登录逻辑结合使用。

您可能还想研究Thymeleaf + Spring Security 集成。这将允许您在模板中获取用户名,而无需在控制器中进行设置。

<div>Logged in user: <span sec:authentication="name"></span></div>

我建议您不要禁用 CSRF 保护。这使您的应用程序容易受到CSRF 攻击使用 Thymeleaf 时会自动包含CSRF 令牌,因此不需要更改代码,您的应用程序将更加安全。


推荐阅读