我們先看spring secuity
的默認(rèn)登錄頁面,
- 加入
springmvc
,spring secuity
抒倚,servlet
的一些依賴,配置jetty的插件,配置端口是8001揽趾,contextPath
是"/"
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>secuity-quickstart-config</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.3.v20170317</version>
<configuration>
<httpConnector>
<port>8001</port>
</httpConnector>
<webApp>
<contextPath>/</contextPath>
</webApp>
</configuration>
</plugin>
</plugins>
</build>
- 定義系統(tǒng)啟動類
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//系統(tǒng)啟動的時候的根類
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{WebAppConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
//設(shè)置成/*表示攔截靜態(tài)的文件
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
- web入口類
/**
*
* 入口類具则,啟動spring mvc,啟動spring secuity
*/
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
- spring security配置類
/**
*
* 初始化spring security
*/
public class WebAppSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
protected String getDispatcherWebApplicationContextSuffix() {
return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
}
}
- 具體的controller
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello spring secuity";
}
@GetMapping("/home")
public String home(){
return "home spring security";
}
@GetMapping("/admin")
public String admin(){
return "admin spring secuity";
}
}
- 權(quán)限用戶名密碼的具體配置
Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
//httpbasee認(rèn)證
http.httpBasic();
}
}
-
默認(rèn)的登錄頁面
http.formLogin();是spring secuity默認(rèn)的登錄頁面啦撮。
自定義登錄
- 先定義一個登錄頁面,將其頁面放在了
WEB-INF
下面的jsp
目錄下汪厨,然后需要在啟動類上加入視圖解析器
@EnableWebMvc
@EnableWebSecurity
@ComponentScan("com.zhihao.miao.secuity")
public class WebAppConfig extends WebMvcConfigurerAdapter {
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置視圖解析器
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp();
}
}
-
Controller
中定義一個url跳轉(zhuǎn)到該登錄頁面
根據(jù)上面的視圖解析器赃春,我們就知道登錄的跳轉(zhuǎn)頁面的路徑是/WEB-INF/jsp/login.jsp
@Controller
public class LoginController {
@GetMapping("/sys/login")
public String login(){
return "/jsp/login";
}
}
- 在
spring security
中配置
登錄的跳轉(zhuǎn)頁面,和登錄的動作url不去做權(quán)限認(rèn)證劫乱。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登錄的跳轉(zhuǎn)頁面织中,和登錄的動作url不應(yīng)該有權(quán)限認(rèn)證锥涕。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
//登錄的時候跳轉(zhuǎn)的登錄頁面url
loginPage("/sys/login").
//登錄頁面提交時候的請求
loginProcessingUrl("/doLogin").
defaultSuccessUrl("/public/login/ok.html"). //如果直接訪問登錄頁面,則登錄成功后重定向到這個頁面狭吼,否則跳轉(zhuǎn)到之前想要訪問的頁面
permitAll(); //就是設(shè)置loginProcessingUrl()也不需要權(quán)限認(rèn)證
}
}
- 登錄頁面:
詳細(xì)的登錄頁面可以查看文章的最后的項目鏈接
<div class="login">
<h1>Login</h1>
<form method="post" action="/doLogin">
<input type="hidden" name="${ _csrf.parameterName}" value="${ _csrf.token}" />
<input type="text" name="username" placeholder="用戶名" />
<input type="password" name="password" placeholder="密碼"/>
<button type="submit" class="btn btn-primary btn-block btn-large">登錄</button>
</form>
</div>
- 測試
訪問localhost:8001/hello
,跳轉(zhuǎn)到http://localhost:8001/sys/login
頁面层坠,具體頁面如下:
- 一些更加細(xì)節(jié)的定制登錄的api使用
比如說失敗重定向(可以在重定向方法中獲取到失敗的異常),失敗跳轉(zhuǎn)刁笙,成功登錄之后重定向等等api的使用
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//賬號被鎖
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").accountLocked(true).roles("GUEST");
//賬號過期
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").accountExpired(true).roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登錄的跳轉(zhuǎn)頁面破花,和登錄的動作url不應(yīng)該有權(quán)限認(rèn)證。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
loginPage("/sys/login").
loginProcessingUrl("/doLogin").
failureForwardUrl("/sys/loginFail"). //使用forward的方式疲吸,能拿到具體失敗的原因,并且會將錯誤信息以SPRING_SECURITY_LAST_EXCEPTION的key的形式將AuthenticationException對象保存到request域中
//failureUrl("/public/login/fail.html"). //失敗重定向,拿不到具體失敗的原因
defaultSuccessUrl("/public/login/ok.html"). //如果直接訪問登錄頁面座每,則登錄成功后重定向到這個頁面,否則跳轉(zhuǎn)到之前想要訪問的頁面
//defaultSuccessUrl("/public/login/ok.html",true). //登錄成功后摘悴,都直接重定向到這個頁面
permitAll();
}
}
比如說重定向拿不到登錄失敗的異常峭梳,而failureForwardUrl()
的api卻可以,點入failureForwardUrl
源碼查看蹂喻,FormLoginConfigurer
的文檔說明葱椭,如果登錄失敗會拋出
SPRING_SECURITY_LAST_EXCEPTION
異常,取到消息可以使用${SPRING_SECURITY_LAST_EXCEPTION.message}
口四,
可以在Controller
層中通過HttpServletRequest
拿到登錄失敗的異常孵运,
@PostMapping("/sys/loginFail")
public String fail(HttpServletRequest req){
AuthenticationException exp = (AuthenticationException)req.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
System.out.println("exp:"+exp.getMessage());
if(exp instanceof BadCredentialsException){
//將錯誤信息放到request域中
req.setAttribute("error_msg", "用戶名或密碼錯誤");
} else if(exp instanceof AccountExpiredException){
req.setAttribute("error_msg", "賬戶過期");
} else if(exp instanceof LockedException){
req.setAttribute("error_msg", "賬戶已被鎖");
}else{
//其他錯誤打印這些信息
System.out.println(exp.getMessage());
}
return "/jsp/login";
}
登錄頁面打印失敗的異常
<div class="login">
<h1>Login</h1>
<form method="post" action="/doLogin">
<input type="hidden" name="${ _csrf.parameterName}" value="${ _csrf.token}" />
<input type="text" name="username" placeholder="用戶名" />
<input type="password" name="password" placeholder="密碼"/>
<button type="submit" class="btn btn-primary btn-block btn-large">登錄</button>
</form>
<div class="login-bottom" style="color:red;">${SPRING_SECURITY_LAST_EXCEPTION.message}</div>
</div>
此時就可以把錯誤信息打印到頁面上
- 還可以自定義登錄成功和失敗的handler進行權(quán)限驗證,自己根據(jù)自己的業(yè)務(wù)代碼來進行定制
通過successHandler
和failureHandler
方法來定義
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("123456").roles("GUEST");
auth.inMemoryAuthentication().withUser("zhihao.miao").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("lisi").password("12345678").roles("USER", "ADMIN");
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/hello").hasRole("GUEST");
http.authorizeRequests().antMatchers("/home").hasRole("USER");
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
//登錄的跳轉(zhuǎn)頁面,和登錄的動作url不應(yīng)該有權(quán)限認(rèn)證窃祝。
http.authorizeRequests().antMatchers("/sys/login").permitAll();
http.authorizeRequests().antMatchers("/**/*.html").permitAll();
http.authorizeRequests().antMatchers("/**/*.css").permitAll();
http.authorizeRequests().antMatchers("/**/*.js").permitAll();
http.authorizeRequests().antMatchers("/**/*.png").access("permitAll");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin().
loginPage("/sys/login").
loginProcessingUrl("/doLogin").
successHandler((request, response, authentication) -> {
//登錄成功的時候跳轉(zhuǎn)到/public/login/ok.html
System.out.println("========登陸成功=======" + authentication.getName());
response.sendRedirect("/public/login/ok.html");
}).failureHandler((request, response, exception) -> {
//登錄失敗的時候跳轉(zhuǎn)到/public/login/fail.html
System.out.println("=======登陸失敗=======" + exception.getMessage());
response.sendRedirect("/public/login/fail.html");
}).permitAll();
}
}