【问题标题】:spring boot security not configured properlyspring boot 安全配置不正确
【发布时间】:2021-02-21 17:16:30
【问题描述】:

我是 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 页面。

【问题讨论】:

    标签: java html spring spring-mvc spring-security


    【解决方案1】:

    从安全配置开始,我将使用 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/**”的请求要求用户登录并具有“ADMIN”角色。
    对任何其他端点的请求都需要用户登录。
    这意味着对“/addReview”的请求需要用户登录。

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

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

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

    由于您在配置中启用了.formLogin(),Spring Security 提供了大部分登录功能。
    您只需将用户名(在本例中为电子邮件)和密码发布到generated "/" login endpoint,框架将验证用户并填充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 integration。这将允许您在模板中获取用户名,而无需在 Controller 中进行设置。

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

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

    【讨论】:

    • 非常感谢您的完整解释 :)。我正在尝试实施您的建议。唯一的一点是我仍然不知道如何填充谁已经登录。当我尝试“Authentication auth = SecurityContextHolder.getContext().getAuthentication();”时在我的控制器中,auth.getName() 为空。
    • 您在哪个端点获取登录用户?这是在他们已经登录之后吗?
    • 我实际上在登录控制器的末尾尝试过,但现在我有点困惑。因为您说我正在使用“formLogin”,它为我做了大部分事情,我看到即使在您的代码中,也没有在后端做任何事情来让用户真正登录,并且您还删除了 loginForm .您能否更清楚地说明我如何才能真正让用户登录?
    • 您能解释一下“您还删除了 loginForm”是什么意思吗?您是指安全配置中的formLogin 吗?我已经编辑了我的答案以包括formLogin 配置。澄清如何让用户登录:如果您有一个自定义表单(例如在“/”处)将用户名和密码发布到“/login”并且用户到达该自定义表单并输入正确的凭据,那么他们将登录。登录后,如果他们向“/addReview”发出任何请求,那么框架将能够检测到他们已登录。
    • 非常感谢。是的,我的意思是安全配置中缺少 formLogin。但是,它仍然无法正常工作。我已经删除了我的表格,只是复制了你的表格,但我认为缺少的部分是控制器部分。我应该在控制器内部做什么才能登录?正如你已经提到的,我现在只是设置视图。我没有做任何事情让用户登录。
    猜你喜欢
    • 2014-02-05
    • 2019-08-25
    • 2016-03-22
    • 2015-02-22
    • 1970-01-01
    • 2014-10-27
    • 2016-02-12
    • 2018-12-05
    相关资源
    最近更新 更多