Spring Security 使用自定義控制器來(lái)完成登陸驗(yàn)證

Spring Security 下面簡(jiǎn)稱為 Security 基于 spring-security 4.1

Security 的 WEB 擴(kuò)展中 form 方式登陸使用的是過(guò)濾器方式,頁(yè)面模版是可以定制的,但是如果需要登陸表單中有更多的選項(xiàng),或者說(shuō)需要在登陸的時(shí)候處理一些事情就變的很不方便。下面就是教你如何使用一個(gè)普通的控制器來(lái)完成登陸驗(yàn)證。

首先是制定 Security 的配置文件

http 節(jié)點(diǎn)中有一個(gè)屬性 entry-point-ref 可以指定如果需要登陸將如何反應(yīng),在實(shí)現(xiàn) AuthenticationEntryPoint 接口的類中有一個(gè)名稱叫 LoginUrlAuthenticationEntryPoint 的類他可以實(shí)現(xiàn)需要登陸的時(shí)候產(chǎn)生 URL 跳轉(zhuǎn)。

首先創(chuàng)建一個(gè) LoginUrlAuthenticationEntryPoint bean

<beans:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <!-- 構(gòu)造函數(shù)中指定需要跳轉(zhuǎn)的 URL -->
    <beans:constructor-arg value="/login" />
</beans:bean>

然后在 http 節(jié)點(diǎn)中配置 entry-point-ref
auto-config 可以關(guān)閉,因?yàn)椴恍枰詣?dòng)配置

<http auto-config="false" entry-point-ref="authenticationEntryPoint">
    ...
</http>

http 節(jié)點(diǎn)內(nèi)添加 intercept-url 防止攔截 /login 鏈接

<intercept-url pattern="/login" access="permitAll" />

如果需要記住我功能,需要在 http 節(jié)點(diǎn)內(nèi)增加 remember-me 配置

<!-- rememberMe 對(duì)應(yīng)的是表單類中的屬性名稱 -->
<remember-me remember-me-parameter="rememberMe" />

最后可以增加一個(gè)退出過(guò)濾器
因?yàn)槭沁^(guò)濾器攔截判斷使用 /logout 不需要有對(duì)應(yīng)的控制器
如果這個(gè) /logout 中沒(méi)有對(duì)應(yīng)的控制器,需要添加 logout-success-url 跳轉(zhuǎn)鏈接,防止訪問(wèn) /logout URL 后候報(bào) 404 錯(cuò)誤

<logout logout-url="/logout" logout-success-url="/" />

最后需要配置 authentication-manager 節(jié)點(diǎn)中的 id 用來(lái)給控制器中注入使用

<authentication-manager id="authenticationManager">
   ...
</authentication-manager>

完整的配置文件代碼

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">
    
    <!-- 本配置文件完全使用自定的控制器來(lái)完成登陸退出操作 Yefei -->
    
    <!-- 開(kāi)啟 Spring Security 調(diào)試模式
    <debug />
    -->

    <!-- 是否開(kāi)啟注解支持,例如: @Secured
    <global-method-security secured-annotations="enabled" />
    -->

    <!-- 配置不需要安全過(guò)濾的頁(yè)面 -->
    <http pattern="/static/**" security="none" />

    <beans:bean id="authenticationEntryPoint"
        class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <beans:constructor-arg value="/login" />
    </beans:bean>

    <http auto-config="false"
        entry-point-ref="authenticationEntryPoint">
        <intercept-url pattern="/" access="permitAll"/>
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
        <intercept-url pattern="/**" access="hasRole('USER')" />
        <!-- 用于 cookie 登陸 remember-me-parameter 中的值必須和表單中的 rememberMe name 一致 -->
        <remember-me remember-me-parameter="rememberMe" />
        <!-- logout 可以使用簡(jiǎn)單的過(guò)濾器完成, 啟用了 CSRF 必須使用 POST 退出 -->
        <logout logout-url="/logout" logout-success-url="/" />
    </http>
    
    <authentication-manager id="authenticationManager">
        <authentication-provider>
            <user-service>
                <user name="admin" authorities="ROLE_USER,ROLE_ADMIN" password="123456" />
                <user name="user" authorities="ROLE_USER" password="123456" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
    
</beans:beans>

下面是控制器的代碼部分

代碼過(guò)程都是通過(guò) debug 模式下在 UsernamePasswordAuthenticationFilter 中分析得出

@Controller
public class AuthController {
    @Autowired
    @Qualifier("authenticationManager") // bean id 在 <authentication-manager> 中設(shè)置
    private AuthenticationManager authManager;
 
    @Autowired
    private SessionAuthenticationStrategy sessionStrategy;
 
    @Autowired(required = false)
    private RememberMeServices rememberMeServices;
 
    @Autowired(required = false)
    private ApplicationEventPublisher eventPublisher;
 
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(LoginForm form) {
        return "login";
    }
 
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String loginPost(@Valid LoginForm form, BindingResult result,
            HttpServletRequest request, HttpServletResponse response) {
        if (!result.hasErrors()) {
 
            // 創(chuàng)建一個(gè)用戶名密碼登陸信息
            UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(form.getUsername(), form.getPassword());
 
            try {
                // 用戶名密碼登陸效驗(yàn)
                Authentication authResult = authManager.authenticate(token);
 
                // 在 session 中保存 authResult
                sessionStrategy.onAuthentication(authResult, request, response);
 
                // 在當(dāng)前訪問(wèn)線程中設(shè)置 authResult
                SecurityContextHolder.getContext().setAuthentication(authResult);
 
                // 如果記住我在配置文件中有配置
                if (rememberMeServices != null) {
                    rememberMeServices.loginSuccess(request, response, authResult);
                }
 
                // 發(fā)布登陸成功事件
                if (eventPublisher != null) {
                    eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
                }
                return "redirect:/";
            } catch (AuthenticationException e) {
                result.reject("authentication.exception", e.getLocalizedMessage());
            }
        }
        return "login";
    }
}

最后是 JSP 頁(yè)面部分代碼

頁(yè)面都需要經(jīng)過(guò) Security 過(guò)濾器才能產(chǎn)生 csrf token,否則請(qǐng)關(guān)閉 csrf

登陸代碼

<form:form commandName="loginForm" method="POST">
    <form:errors path="" />
    <p>用戶名:<form:input path="username"/> <form:errors path="username" /></p>
    <p>密碼:<form:input path="password"/> <form:errors path="password" /></p>
    <p>記住我:<form:checkbox path="rememberMe"/> <form:errors path="rememberMe" /></p>
    <button type="submit">登陸</button>
</form:form>

退出代碼

<form action="/logout" method="POST">
    <sec:csrfInput/>
    <button>退出</button>
</form>

原創(chuàng)文章轉(zhuǎn)載注明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評(píng)論 6 342
  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,399評(píng)論 22 257
  • 引言: 本文系《認(rèn)證鑒權(quán)與API權(quán)限控制在微服務(wù)架構(gòu)中的設(shè)計(jì)與實(shí)現(xiàn)》系列的完結(jié)篇,前面三篇已經(jīng)將認(rèn)證鑒權(quán)與API權(quán)...
    aoho閱讀 3,251評(píng)論 0 16
  • 東方的吐白做黎明的禱告者 陽(yáng)光的照耀祝福每一個(gè)相信幸福的人 金燦燦撒下在河面上,泛著魚(yú)鱗的形狀 清脆的鳥(niǎo)聲徘徊在樹(shù)...
    鹿宥宥閱讀 156評(píng)論 0 5

友情鏈接更多精彩內(nèi)容