Spring Security
思維導圖

簡介
SpringSecurity是Spring下的一個安全架構,與shiro 類似,一般用于使用者認證(Authentication)和使用者授權(Authorization)兩個部分,常與與SpringBoot相整合。
初階 Spring Security
添加maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
重新部署,會看到一個登陸頁面。
密碼,翻看日志可以檢視到
使用者名預設為 user
輸入使用者名和密碼
登入成功
中階 Security 記憶體使用者名和密碼
使用者名密碼登入
在上面的章節中,添加了Security依賴,直接就出現了登入界面,這次讓使用者名,密碼儲存到記憶體當中
寫一個extends WebSecurityConfigurerAdapter的配置類:
配置類内容如下
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//密碼加密函數
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//password為BCryptPasswordEncoder加密123後的值
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("$2a$10$2cPRItUHyE1GSZnrYWHiQevpbxn4ikWgOa1PYL5miWvqK8GFVCWb6").roles("admin")
.and().withUser("java").password("$2a$10$rygGQylvmoAFmPcKQP6xvepNVAw9Bxp0sbAphxKQwhAV79Au0ECvq").roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasAnyRole("admin","user")
.anyRequest().authenticated() //其他路徑認證之後就可以通路
.and()
.formLogin()
.loginProcessingUrl("/doLogin") //處理登入請求的位址
.permitAll()
.and()
.csrf().disable();
}
}
輸入使用者名,密碼,進行登入
可以看到,登入成功
其中,
1.通過 @EnableWebSecurity注解開啟Spring Security的功能。使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個注解,可以開啟security的注解,我們可以在需要控制權限的方法上面使用@PreAuthorize,@PreFilter這些注解。
2.extends 繼承 WebSecurityConfigurerAdapter 類,并重寫它的方法來設定一些web安全的細節。我們結合@EnableWebSecurity注解和繼承WebSecurityConfigurerAdapter,來給我們的系統加上基于web的安全機制。
## 角色權限控制
當我們的系統功能子產品當需求發展到一定程度時,會不同的使用者,不同角色使用我們的系統。這樣就要求我們的系統可以做到,能夠對不同的系統功能子產品,開放給對應的擁有其通路權限的使用者使用。
Spring Security提供了Spring EL表達式,允許我們在定義URL路徑通路(@RequestMapping)的方法上面添加注解,來控制通路權限。
在标注通路權限時,根據對應的表達式傳回結果,控制通路權限:
true,表示有權限
fasle,表示無權限
建立新的控制器
package com.example.demo.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class Test {
@RequestMapping("/mapping")
@ResponseBody
public String test(){
return "test";
}
}
可以看到已經通路成功
添權重限注解
package com.example.demo.web;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class Test {
@RequestMapping("/mapping")
@ResponseBody
@PreAuthorize("hasRole('admin')") // Spring Security預設的角色字首是”ROLE_”,使用hasRole方法時已經預設加上了
public String test(){
return "test";
}
}
此時通路
403錯誤
高階: 使用資料庫儲存使用者名和密碼
使用者表
@Entity
class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@BeanProperty
var id: Integer = _
@BeanProperty
var userName: String = _
@BeanProperty
var password: String = _
}
角色表
···
@Entity
class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@BeanProperty
var id: Integer = _
var role: String = _
}
使用者角色表
@Entity
class UserRole {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@BeanProperty
var id: Integer = _
@BeanProperty
var userId: Integer = _
@BeanProperty
var roleId: Integer = _
}
更改SpringSecurity
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("$2a$10$2cPRItUHyE1GSZnrYWHiQevpbxn4ikWgOa1PYL5miWvqK8GFVCWb6").roles("admin")
.and().withUser("java").password("$2a$10$rygGQylvmoAFmPcKQP6xvepNVAw9Bxp0sbAphxKQwhAV79Au0ECvq").roles("user");
更改為
@Override
@Bean
public UserDetailsService userDetailsService() { //覆寫寫userDetailsService方法 (1)
return new LightSwordUserDetailService();
}
//AuthenticationManager使用我們的 lightSwordUserDetailService 來擷取使用者資訊
auth.userDetailsService(userDetailsService());
具體的 new LightSwordUserDetailService(); 實作類,需要自己進行配置
這個接口需要我們實作一個方法:loadUserByUsername。即從資料庫中取出使用者名、密碼以及權限相關的資訊。最後傳回一個UserDetails 實作類。
@Service
class LightSwordUserDetailService extends UserDetailsService {
@Autowired var userRoleDao: UserRoleDao = _
@Autowired var userDao: UserDao = _
@Autowired var roleDao: RoleDao = _
override def loadUserByUsername(username: String): UserDetails = {
// val user = userDao.findByUsername(username) // 直接調用jpa自動生成的方法
val user = userDao.getUserByUsername(username)
if (user == null) throw new UsernameNotFoundException(username + " not found")
val authorities = new util.ArrayList[SimpleGrantedAuthority]
val userRoles = userRoleDao.listByUserId(user.id)
// Scala中調用java的collection類,使用scala的foreach,編譯器會提示無法找到result的foreach方法。因為這裡的userRoles的類型為java.util.List。若要将其轉換為Scala的集合,就需要增加如下語句:
import scala.collection.JavaConversions._
for (userRole <- userRoles) {
val roleId = userRole.roleId
val roleName = roleDao.findOne(roleId).role
if (!StringUtils.isEmpty(roleName)) {
authorities.add(new SimpleGrantedAuthority(roleName))
}
System.err.println("username is " + username + ", " + roleName)
}
new User(username, user.password, authorities)
}
}
在上方,通過内部類的方式,擷取到了一個User對象。并添加進入了UserDetails中
這樣就完成了高階教程