開啓SpringBoot項目登錄校驗
在SpringBoot項目中引入SpringSecurity的依賴之後,項目就會默認開始基於HttpBasic的校驗,用戶默認爲user
,密碼在項目啓動的時候會在控制檯中進行打印,隨便寫一個controller啓動並對其進行訪問,會被攔截並彈出登錄窗口
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
默認表單登錄
在實際項目中很少使用Http的登錄,接下來介紹一下如何開啓SpringSecurity提供的默認表單登錄
創建一個WebSecurityConfig
繼承WebSecurityConfigurerAdapter
,重寫入參爲HttpSecurity http
的configure方法
/**
* @Auther: Anntly
* @Date: 2020/4/2 09:20
* @Description:
*/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 讓所有請求都需要權限驗證
.anyRequest().authenticated()
.and()
// 開啓表單登錄
.formLogin();
}
}
注意要開啓@EnableWebSecurity
註解,該註解組合了@Configuration
會將其作爲配置文件注入Spring容器中,
使用自定義登錄界面
默認提供的登錄界面有些簡陋,一般都會開發我們自己的登錄界面,我是直接去模板網站找的登錄界面,其中核心的頁面代碼如下,前端代碼以及資源放在resource/sstatic
下
<form action="/login" method="POST">
<div class="form-group">
<label for="username">User-Name</label>
<input id="username" class="form-control" name="username" value="" required autofocus>
</div>
<div class="form-group">
<label for="password">Password
<a href="forgot.html" class="float-right">
Forgot Password?
</a>
</label>
<input id="password" type="password" class="form-control" name="password" required
data-eye>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="remember"> Remember Me
</label>
</div>
<div class="form-group no-margin">
<button type="submit" class="btn btn-primary btn-block">
Login
</button>
</div>
<div class="margin-top20 text-center">
Don't have an account? <a href="register.html">Create One</a>
</div>
</form>
在代碼地址中可以查看詳細代碼,其中form的action的訪問地址定義爲/login
,這個需要在配置文件進行配置
修改上一步的WebSecurityConfig
文件
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 對靜態資源放行
.antMatchers("/css/**", "/img/**", "/js/**", "/bootstrap/**").permitAll()
.anyRequest().authenticated()
.and()
// 指定登錄界面
.formLogin().loginPage("/myLogin.html")
// 指定處理登錄的請求url
.loginProcessingUrl("/login")
// 對登錄相關頁面請求放行
.permitAll()
// 使登錄頁不受限
.and()
// 關閉csrf
.csrf().disable();
}
antMatchers使用了ANT模式,其中?
表示匹配任意單個字符,*
表示匹配0或者任意數量的字符,**
表示匹配0或更多的目錄
重啓項目後,再次訪問請求就能夠進入到自定義的登錄界面
使用內存用戶
每次用戶密碼打印到控制檯是不合理的,呼應本章的標題,接下來在內存中配置我們的用戶;在之前我們重寫的configure方法入參爲HttpSecurity
,接下來再重寫一個入參爲AuthenticationManagerBuilder
的
/**
* @Auther: Anntly
* @Date: 2020/4/2 09:20
* @Description:
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 開啓註解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 對靜態資源放行
.antMatchers("/css/**", "/img/**", "/js/**", "/bootstrap/**").permitAll()
.anyRequest().authenticated()
.and()
// 指定登錄界面
.formLogin().loginPage("/myLogin.html")
// 指定處理登錄的請求url
.loginProcessingUrl("/login")
// 對登錄相關頁面請求放行
.permitAll()
// 使登錄頁不受限
.and()
// 關閉csrf
.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 配置兩個用戶,user和admin,密碼均爲123,分別持有角色USER和ADMIN
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user")
.password(passwordEncoder().
encode("123")).roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().
encode("123")).roles("ADMIN");
}
}
在代碼中可以看到引入了BCryptPasswordEncoder
這個類,這個類主要用於對密碼的加密,在SpringSecurity5之後必須對密碼進行加密後才能使用,Spring默認並未選用MD5加密的方式,而是選用了這種,因爲這種方式加密選用的隨機salt進行加密,相同的字符串加密出來的結果也不一樣,也就避免了MD5撞庫的危險
除了用戶密碼之外還配置了角色,在配置類上還添加了@EnableGlobalMethodSecurity(prePostEnabled = true)
用戶開啓方法權限校驗,即訪問對應的controller需要對應的角色,在controller中使用如下
@GetMapping("/admin")
@PreAuthorize("hasAnyRole('ADMIN')")
public String api(){
return "hello admin";
}
@GetMapping("/user")
@PreAuthorize("hasAnyRole('USER')")
public String api(){
return "hello user";
}
接下里重啓項目,登錄user用戶
訪問user路徑,可以看到hello user
再訪問admin,會發現無法訪問,異常爲403即未授權,401爲登錄驗證失敗
再登錄admin後訪問admih,可以看到能夠看到hello admin
至此基於內存的登錄方式講解的差不多了,除此之外,現在基本上是前後端分離的方式,後端校驗後一般是返回前端json字符串,這個可以用成功/失敗處理器(successHandler/failureHandler)來實現,可以使用類實現了再注入,下面爲了方便演示直接就內部類實現了
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/bootstrap/**").permitAll()
.antMatchers("/app/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/myLogin.html")
.loginProcessingUrl("/login")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"0\",\"message\":\"歡迎登陸系統\"}");
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"error_code\":\"401\",\"name\"" + e.getClass() +
"\",\"message\":\":\"" + e.getMessage() + "\"}");
}
})
.permitAll()
// 使登錄頁不受限
.and()
.csrf().disable();
}