Spring Boot 无法登录

Posted

技术标签:

【中文标题】Spring Boot 无法登录【英文标题】:Unable to Login in Spring Boot 【发布时间】:2021-11-08 18:46:37 【问题描述】:

我是 Spring Boot 新手,我正在尝试登录我的应用程序。我面临一些问题。 我无法登录。它无法验证以使用我的凭据登录并返回消息登录无效。 我希望用户在尝试访问客户端站点(例如 localhost:8080/)时进行身份验证。当用户对链接文件无效时,我还想实现注销。 这是我的主要应用程序

    package oidc.controller;

import eu.olympus.client.interfaces.UserClient;
import eu.olympus.model.Attribute;
import eu.olympus.model.AttributeIdentityProof;
import eu.olympus.model.Operation;
import eu.olympus.model.Policy;
import eu.olympus.model.Predicate;
import eu.olympus.model.exceptions.AuthenticationFailedException;
import eu.olympus.model.exceptions.ExistingUserException;
import eu.olympus.model.exceptions.OperationFailedException;
import eu.olympus.model.exceptions.TokenGenerationException;
import eu.olympus.model.exceptions.UserCreationFailedException;
import eu.olympus.model.server.rest.IdentityProof;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import oidc.model.AttributeContainer;
import oidc.model.ChangeAttributesRequest;
import oidc.model.ChangePasswordRequest;
import oidc.model.CreateUserRequest;
import oidc.model.DeleteAccountRequest;
import oidc.model.LoginRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.RedirectView;

@Controller
public class OidcController 

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

    @Autowired
    UserClient userClient;

    @Autowired
    Policy policy;

    // Login
    @RequestMapping("/login")
    public String login(Model model, @RequestParam String redirect_uri, @RequestParam String state, @RequestParam String nonce, HttpServletRequest request) 
        request.getSession().setAttribute("redirectUrl", redirect_uri);
        request.getSession().setAttribute("state", state);
        request.getSession().setAttribute("nonce", nonce);
        LoginRequest loginRequest = new LoginRequest();
        model.addAttribute("loginRequest", loginRequest);
        policy.setPolicyId(nonce);
        return "/login";
    

    @RequestMapping("/loginFailed")
    public String login(Model model) 
        LoginRequest loginRequest = new LoginRequest();
        model.addAttribute("loginRequest", loginRequest);
        model.addAttribute("loginError", true);
        return "/login";
    

    @RequestMapping("/loginPage")
    public String loginPage(Model model) 
        LoginRequest loginRequest = new LoginRequest();
        model.addAttribute("loginRequest", loginRequest);
        model.addAttribute("hasCreated", false);
        return "/login";
    

    @PostMapping("/authenticate")
    public RedirectView authenticate(LoginRequest loginRequest, Model model, HttpServletRequest request) throws AuthenticationFailedException, TokenGenerationException 
        try 

            // TODO We need to get the audience somehow?
            policy.getPredicates().add(new Predicate("audience", Operation.REVEAL, new Attribute("olympus-service-provider")));

            
            
            String token = userClient.authenticate(loginRequest.getUsername(), loginRequest.getPassword(), policy, null, "NONE");
            model.addAttribute("username", loginRequest.getUsername());
            model.addAttribute("token", token);
            
            String redirectUrl = (String) request.getSession().getAttribute("redirectUrl");
            String state = (String) request.getSession().getAttribute("state");
            return new RedirectView(redirectUrl + "#state=" + state + "&id_token=" + token + "&token_type=bearer");
         catch (Exception e) 
            e.printStackTrace();
            if (ExceptionUtils.indexOfThrowable(e, AuthenticationFailedException.class) != -1) 
                return new RedirectView("/loginFailed", true);
             else 
                throw e;
            
         finally 
            userClient.clearSession();
        
    

这里是登录请求

package oidc.model;

import lombok.Getter;
import lombok.Setter;

/**
 * A container for a login request
 */
@Getter
@Setter
public class LoginRequest 

    private String username;
    private String password;

【问题讨论】:

如果您还没有使用 Spring Security,我建议您考虑将其添加到您的项目中,因为它默认提供登录功能。您可以将其中一个示例用作starting point,并查看参考documentation 【参考方案1】:

我建议你使用Spring Security。它是一个依赖项,您可以通过您的构建工具添加它,例如MavenGradle 等。研究您的代码后,我可以看到您正在尝试从头开始构建安全机制。

我不建议您这样做,除非您有很高的动机这样做。如果您可以使用Spring Security,它非常强大,并且配备了您正在寻找的所有功能。您可以轻松克服用户身份验证、授权,甚至可以提供默认登录页面。

在身份验证方面,您可以拥有几种类型的用户存储,例如内存用户存储、JDBC 用户存储、LDAP 用户存储甚至您自己的自定义用户存储。除了通过GUI 进行用户名和密码身份验证外,您还可以进行系统到系统身份验证。只需添加过滤器和次要配置等几个步骤,即可轻松实现JWT 令牌认证。

在这里很难涵盖并给出整个源代码作为答案,但我会为您提供一个示例代码,以便您可以一睹它的风采。请注意,以下代码仅用于演示目的,可以根据您的标准对其进行修改。

Spring 安全配置类

package com.example.sankalpaspringbootcicd.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * Security configuration class.
 * Created date - 2021/08/02
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    /**
     * Authentication
     * Responsible for configuring user-store.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theAuthenticationManager AuthenticationManagerBuilder
     * @throws Exception - Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder theAuthenticationManager) throws Exception 
        theAuthenticationManager.inMemoryAuthentication()
                //Admin user
                .withUser("admin")
                .password("super")
                .roles("ADMIN")
                .and()
                //Normal user
                .withUser("user")
                .password("password")
                .roles("USER");
    

    /**
     * Authorization
     * Responsible for security configuration.
     * Overridden from WebSecurityConfigurerAdapter level.
     * @param theHttpSecurity HttpSecurity
     * @throws Exception - Exception
     */
    @Override
    public void configure(HttpSecurity theHttpSecurity) throws Exception 
        theHttpSecurity.csrf().disable();
        theHttpSecurity
                .authorizeRequests()
                .antMatchers("/welcome/**").access("permitAll")
                .antMatchers("/user/**").hasRole("ADMIN")
                .anyRequest().fullyAuthenticated() //All requests should be authenticated
                .and().headers().frameOptions().sameOrigin() //To allow H2-Console
                .and().httpBasic();
    

    /**
     * Method constructing a password encoder bean.
     * Constructs 'NoOpPasswordEncoder'.
     * @return PasswordEncoder
     */
    @Bean
    public PasswordEncoder getPasswordEncoder() 
        return NoOpPasswordEncoder.getInstance();
    


这是一个非常简单的示例,我将带您了解每种方法。第一种方法是configure(AuthenticationManagerBuilder theAuthenticationManager),它的作用基本上是为您创建一个内存用户存储。

第二种方法是configure(HttpSecurity theHttpSecurity),它允许您对自己的安全行为进行自定义。它确实为每个人提供了一些路由,仅对具有特定角色的用户限制了一些路由,允许 H2 嵌入式数据库控制台的路由,禁用 CSRF(跨站点请求伪造)等。这也会提示您一个默认登录页面.您还可以在此处进一步添加与您的登录和注销机制相关的逻辑。

第三种方法是PasswordEncoder getPasswordEncoder(),它确实创建了一个密码编码器bean并将其放在Spring Application Context中以供随时使用。

【讨论】:

以上是关于Spring Boot 无法登录的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 和 OAuth2 社交登录,无法获取 refreshToken

为啥我登录后无法进入管理 REST API 请求映射 - Spring Boot

Spring Boot:无法登录 h2 控制台

将cookie设置为安全时Spring Boot无法登录

如何解决在使用 Spring Boot 和 Spring Security 启用 CSRF 后无法正常工作的登录问题?

在 Spring Boot 安全性中,无法跳过登录 url 的 OncePerRequestFilter 过滤器(基本上是在初始登录期间获取 JWT 令牌)