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)載注明