Spring Security 使用自定義控制器來完成登陸驗證

Spring Security 下面簡稱為 Security 基于 spring-security 4.1

Security 的 WEB 擴(kuò)展中 form 方式登陸使用的是過濾器方式,頁面模版是可以定制的艾杏,但是如果需要登陸表單中有更多的選項涤躲,或者說需要在登陸的時候處理一些事情就變的很不方便。下面就是教你如何使用一個普通的控制器來完成登陸驗證虱咧。

首先是制定 Security 的配置文件

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

首先創(chuàng)建一個 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)閉腕巡,因為不需要自動配置

<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 對應(yīng)的是表單類中的屬性名稱 -->
<remember-me remember-me-parameter="rememberMe" />

最后可以增加一個退出過濾器
因為是過濾器攔截判斷使用 /logout 不需要有對應(yīng)的控制器
如果這個 /logout 中沒有對應(yīng)的控制器,需要添加 logout-success-url 跳轉(zhuǎn)鏈接,防止訪問 /logout URL 后候報 404 錯誤

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

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

<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">
    
    <!-- 本配置文件完全使用自定的控制器來完成登陸退出操作 Yefei -->
    
    <!-- 開啟 Spring Security 調(diào)試模式
    <debug />
    -->

    <!-- 是否開啟注解支持煎楣,例如: @Secured
    <global-method-security secured-annotations="enabled" />
    -->

    <!-- 配置不需要安全過濾的頁面 -->
    <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 可以使用簡單的過濾器完成, 啟用了 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>

下面是控制器的代碼部分

代碼過程都是通過 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)建一個用戶名密碼登陸信息
            UsernamePasswordAuthenticationToken token =
                new UsernamePasswordAuthenticationToken(form.getUsername(), form.getPassword());
 
            try {
                // 用戶名密碼登陸效驗
                Authentication authResult = authManager.authenticate(token);
 
                // 在 session 中保存 authResult
                sessionStrategy.onAuthentication(authResult, request, response);
 
                // 在當(dāng)前訪問線程中設(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 頁面部分代碼

頁面都需要經(jīng)過 Security 過濾器才能產(chǎn)生 csrf token豺总,否則請關(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)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市择懂,隨后出現(xiàn)的幾起案子喻喳,更是在濱河造成了極大的恐慌,老刑警劉巖困曙,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件表伦,死亡現(xiàn)場離奇詭異,居然都是意外死亡慷丽,警方通過查閱死者的電腦和手機(jī)蹦哼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來要糊,“玉大人纲熏,你說我怎么就攤上這事〕恚” “怎么了局劲?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奶赠。 經(jīng)常有香客問我鱼填,道長,這世上最難降的妖魔是什么车柠? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任剔氏,我火速辦了婚禮,結(jié)果婚禮上竹祷,老公的妹妹穿的比我還像新娘谈跛。我一直安慰自己,他們只是感情好塑陵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布感憾。 她就那樣靜靜地躺著,像睡著了一般令花。 火紅的嫁衣襯著肌膚如雪阻桅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天兼都,我揣著相機(jī)與錄音嫂沉,去河邊找鬼。 笑死扮碧,一個胖子當(dāng)著我的面吹牛趟章,可吹牛的內(nèi)容都是我干的杏糙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蚓土,長吁一口氣:“原來是場噩夢啊……” “哼宏侍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜀漆,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤谅河,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后确丢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绷耍,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年蠕嫁,在試婚紗的時候發(fā)現(xiàn)自己被綠了锨天。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡剃毒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搂赋,到底是詐尸還是另有隱情赘阀,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布脑奠,位于F島的核電站基公,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宋欺。R本人自食惡果不足惜轰豆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望齿诞。 院中可真熱鬧酸休,春花似錦、人聲如沸祷杈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽但汞。三九已至宿刮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間私蕾,已是汗流浹背僵缺。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踩叭,地道東北人磕潮。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揉抵。 傳聞我的和親對象是個殘疾皇子亡容,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容