Spring Security UserNotFoundException 在带有 JSP 视图模板的 Spring Boot 中不显示错误消息

Posted

技术标签:

【中文标题】Spring Security UserNotFoundException 在带有 JSP 视图模板的 Spring Boot 中不显示错误消息【英文标题】:Spring security UserNotFoundException not show error message in Spring Boot with JSP view template 【发布时间】:2020-09-28 06:36:21 【问题描述】:

我正在使用 Spring Boot 和 JSP 创建一个 Web 应用程序。我已经实现了 Spring Security 并且我正在覆盖 loadUserByUsername() 方法,当找不到用户时,我会抛出 UsernameNotFoundException("User not found") 一条消息。现在我想在我的登录页面中显示一条正确的消息,我该如何实现?

MyUserDetailsS​​ervice.java

    package in.bushansirgur.security.service;

    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;

    import javax.transaction.Transactional;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.SpringSecurityMessageSource;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;

    import in.bushansirgur.security.entity.Role;
    import in.bushansirgur.security.entity.User;

    @Service
    public class MyUserDetailsService implements UserDetailsService 

        @Autowired
        private UserService userService;

        @Override
        @Transactional
        public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException
            User user = userService.findUserByEmail(email);
            if(user == null || user.getActive() == null)
                throw new UsernameNotFoundException("User not found!");
            

            List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
            return buildUserForAuthentication(user, authorities);
        
        ...
    

LoginController.java

    @RequestMapping(value = "/login", "/")
        public ModelAndView login(@RequestParam(name="error", required = false)String error, 
                @RequestParam(name="logout", required = false)String logout, HttpServletRequest request
                ) throws UnknownHostException      
            // Port
            String portNumber = environment.getProperty("server.port");
            // Local address
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            String hostName = InetAddress.getLocalHost().getHostName();
            System.out.println("Host address:"+hostAddress+" Host name:"+hostName+" Port number:"+portNumber);
            // Remote address
            InetAddress.getLoopbackAddress().getHostAddress();
            InetAddress.getLoopbackAddress().getHostName();
            ModelAndView mv = new ModelAndView("login");

            if(error!=null) 
                mv.addObject("message", "Invalid Username and Password!");
            

            if(logout!=null) 
                mv.addObject("logout", "User has successfully logged out!");
            
            System.out.println("context path:"+request.getContextPath());
            return mv;              
        

login.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    <c:set var="contextRoot" value="$pageContext.request.contextPath" />
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <style>
    .help-block 
        color: #ff0000;
    
    </style>
    <body>

        <h1>Login</h1>

        <c:if test="$not empty message">
                        <div class="row">

                            <div class="col-md-offset-3 col-md-6">

                                <div class="alert alert-danger">$message</div>

                            </div>
                        </div>

                    </c:if>
                    <c:if test="$not empty logout">
                        <div class="row">

                            <div class="col-md-offset-3 col-md-6">

                                <div class="alert alert-success">$logout</div>

                            </div>
                        </div>

                    </c:if>
        <form action="login" method="post" id="loginform">
            <input type="text" name="email" placeholder="Enter email" /><br/>
            <input type="password" name="password" placeholder="Enter password" /><br/>
            <button type="submit">Login</button>
            <%-- <input type="hidden" name="$_csrf.parameterName" value="$_csrf.token" /> --%>
        </form>

        <a href="$contextRoot/registration">Register</a>
        <script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.17.0/dist/jquery.validate.js"></script>
        <script>
            $(function()
                const $loginForm = $("#loginform");

                if($loginForm.length)

                    $loginForm.validate(

                        rules: 

                            email: 
                                required: true
                            ,

                            password: 
                                required: true
                            
                        ,

                        messages: 

                            email: 
                                required: "Please enter email"
                            ,

                            password: 
                                required: "Please enter password"
                            
                        ,
                        errorElement: 'em',
                        errorPlacement: function(error, element) 
                            error.addClass('help-block');
                            error.insertAfter(element);
                        

                    )

                
            )
        </script>
    </body>
    </html>

WebSecurityConfiguration.java

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

        @Autowired
        private BCryptPasswordEncoder bCryptPasswordEncoder;

        @Autowired
        private MyUserDetailsService userDetailsService;

        @Override
        protected void configure(HttpSecurity http) throws Exception 
            http.
            authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/login").permitAll()
            .antMatchers("/registration").permitAll()
            .antMatchers("/confirm-account").permitAll()
            .antMatchers("/admin/**").hasAuthority("ADMIN")
            .antMatchers("/user/**").hasAnyAuthority("ADMIN", "USER")
            .anyRequest().authenticated().and().csrf().disable().formLogin()
            .loginPage("/login")//.failureUrl("/login?error=true")
            .defaultSuccessUrl("/dashboard")
            .usernameParameter("email")
            .passwordParameter("password")
            .and().exceptionHandling().accessDeniedPage("/403");
        
        ...
    

JSP 中显示的错误消息是“无效的用户名和密码”。那么如果抛出UserNotFoundException,如何显示“找不到用户”之类的错误消息!

提前致谢!

【问题讨论】:

不要显示该消息,因为这是一个安全风险。只显示一般消息。 那么,我现在显示的用户名和密码无效,可以吗? 【参考方案1】:

默认情况下,如果找不到用户名或密码不正确,AbstractUserDetailsAuthenticationProvider 会抛出 BadCredentialsException。 将此属性设置为 false 将导致 UsernameNotFoundExceptions 被抛出而不是前者。

请注意,对于这两个异常,这被认为不如抛出 BadCredentialsException 安全。为此,您必须实例化您的身份验证提供程序,如下所示:

  @Bean
public AuthenticationProvider daoAuthenticationProvider() 
    DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
    impl.setUserDetailsService(yourUserDetailsService());
    impl.setHideUserNotFoundExceptions(false) ;
    return impl ;

警告:这样做不是很好的安全做法。

见here、setHideUserNotFoundExceptions和spring security bad credentials distinguish between invalid username or password

【讨论】:

以上是关于Spring Security UserNotFoundException 在带有 JSP 视图模板的 Spring Boot 中不显示错误消息的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security:2.4 Getting Spring Security

没有 JSP 的 Spring Security /j_spring_security_check

Spring-Security

Spring Security 登录错误:HTTP 状态 404 - /j_spring_security_check

未调用 Spring Security j_spring_security_check

Spring Security入门(3-7)Spring Security处理页面的ajax请求