除了利用框架内提供的默认异常处理,也可以自定义,实现 AuthenticationFailureHandler 接口即可

自定义认证失败处理

MyAuthenticationFailureHandler.kt

package com.example.springsecuritydemo.security

import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.authentication.AuthenticationFailureHandler
import java.nio.charset.StandardCharsets

class MyAuthenticationFailureHandler : AuthenticationFailureHandler {

    override fun onAuthenticationFailure(request: HttpServletRequest?, response: HttpServletResponse?, exception: AuthenticationException?) {
        response?.setHeader("Content-Type", "text/html")
        response?.characterEncoding = StandardCharsets.UTF_8.name()
        request?.session?.setAttribute("loginpost_errors", exception?.message)
        response?.sendRedirect("/login")
    }
}

配置在 formLogin

formLogin {
// 登录页面(get请求)
    loginPage = "/login"
    // failureUrl没必要配置,框架默认将 loginPage+"?error"当成failureUrl
    // failureUrl = "/login"
    // 登录提交地址(post请求)
    loginProcessingUrl = "/loginpost"
    // 自定义异常处理
    authenticationFailureHandler = MyAuthenticationFailureHandler()
    // 默认登录成功跳转的地址
    defaultSuccessUrl("/", true)
    // 以上设置的两个地址(/login, /loginpost)全都直接放行,不需要权限
    permitAll = true
}

效果

自定义权限验证异常处理

默认的没权限页面如下这样

可以看到非常的不好看,在配置httpSecurity时,还有一个 exceptionHandling 属性,可以给它配置一个403的页面

exceptionHandling {
    accessDeniedPage = "/403.html"
}
csrf { disable() }

src/resources//static 里创建一个403.html页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>403</title>
</head>
<body>
<p>没有权限!!!</p>
</body>
</html>

别忘了将这个地址放行

authorizeHttpRequests {
    authorize("/403.html", permitAll)
    //...
}

使用 zhangsan 帐号登录,访问 /user/list

服务端渲染的页面这样就够了,但现在都喜欢前后端分离,所以自定义异常处理还是有必要的

创建 MyAccessDeniedHandler 类实现 接口 AccessDeniedHandler

package com.example.springsecuritydemo.security

import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.web.access.AccessDeniedHandler
import java.nio.charset.StandardCharsets

class MyAccessDeniedHandler : AccessDeniedHandler {
    override fun handle(request: HttpServletRequest?, response: HttpServletResponse?, accessDeniedException: AccessDeniedException?) {
        response?.setHeader("Content-Type", "text/html")
        response?.characterEncoding = StandardCharsets.UTF_8.name()
        response?.writer?.write(
            """
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>403</title>
            </head>
            <body>
            <p>自定义没权限的异常页面!!!</p>
            </body>
            </html>
        """.trimIndent()
        )
        response?.writer?.flush()
    }
}

配置 httpSecurity里的exceptionHandling

exceptionHandling {
    // accessDeniedPage = "/403.html"
    accessDeniedHandler = MyAccessDeniedHandler()
}

测试效果如下

总结

  • 如果网站是服务端渲染的话,用框架自带的异常处理就够了,如果是前后端分离的话,可以自定义,会更灵活