SpringSecurity核心功能:認證、授權、攻擊防護(防止偽造身份)
涉及的依賴如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
新建一個項目,添加如上依賴【添加依賴之后默認開始授權驗證】,在控制器controller中測試,指定url,比如
@Controller
public class UserController {
@RequestMapping(value="/hello")
@ResponseBody
public String hello(){return "=======Welcome to HelloWorld==============";}
}
如上,原本啟動項目后,在地址欄中輸入http://localhost:8080/hello應該顯示返回的內容
然而此次加了安全驗證后,不管url中訪問的地址是什么,hello還是hello111,均返回login頁面,如下
此時系統都沒有連DB,用戶名和密碼是什么?
控制台中有消息,比如Using generated security password: 76dade1c-f190-44f8-915c-7a6b6917fb9a【每次隨機生成的密碼】
將用戶名 user 和 密碼 76dade1c-f190-44f8-915c-7a6b6917fb9a 填入上面對話框中,點擊按鈕Sign in
若之前訪問的頁面是控制器中配置的頁面http://localhost:8080/hello
則此時能成功顯示
若之前訪問的頁面是其他的,控制器中未配置的,則重定向后返回頁面不存在。
當前自己的項目中,總不能用系統生成的密碼進行登錄獲得權限,那不要被別人笑死。
進階階段:
我簡單創建了一張表,希望該表的人輸入匹配的用戶名和密碼后,方能登錄。
CREATE TABLE `admin_user`(
`id` int(4) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(100),
`password` VARCHAR(100),
`role` VARCHAR(100),
`realname` VARCHAR(100),
`mobile` VARCHAR(2000),
`state` BIT default 0,
`info` VARCHAR(200),
PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=300;
塞了幾條數據進去,然后我希望用戶在頁面上進行登錄,那我必須還要創建一個User對象,所謂登錄就是傳入username和password匹配的場景,只要匹配,就登錄成功,跳轉到之前的url
public class User {
private int id;
private String name;
private String password; 省略 getter and setter}
public interface UserService {
User login(String name, String password);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public User login(String name, String password) {
String sql ="select * from admin_user where username =? and password = ?";
User user =jdbcTemplate.queryForObject(sql,new UserRowMapper(),name,password);
return user;
}
}
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
//此處要使用表中的字段,不能使用屬性
int id =resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
//String role = resultSet.getString("role");
User user = new User();
user.setId(id);
user.setName(username);
user.setPassword(password);
return user;
}
}
登錄的方法啪啪啪很快就寫好了,我要怎么讓系統知道,所有的請求,要先進行登錄呢,登錄的URL是什么?
先看看別人的代碼,貌似是實現了UserDetailsService 接口,而點進去發現該接口就一個方法
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
通過一個String類型的變量val1,獲取用戶的詳細信息。。。怎么跟我想的不太一樣?
再點進去發現UserDetails 也是一個接口
package org.springframework.security.core.userdetails;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
一個集合,收集權限,結合做過的項目,有的權限是超級管理員,有的權限是普通管理員,又或者有的刪,有新增,有更新等等權限;兩個返回String的方法;
還有判斷賬戶是否過期,被鎖,驗證是否過期,是否開啟了。。。
看來光看別人的代碼,還是丈二和尚摸不着頭腦呢,去看看官方文檔吧
https://spring.io/projects/spring-security
https://spring.io/guides/topicals/spring-security-architecture
英文原文我就不粘貼了,翻譯過來,大意就是:
應用程序權限歸結於兩個獨立的問題:
1. 你是誰
2. 你有什么樣的權限
一般叫法是權限控制 或者 授權
下面開始講框架中的源碼,通過看源碼可以了解設計的思路
1. 授權策略中主要的接口是AuthenticationManager
,並且只有一個方法
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
驗證管理員在方法authenticate()可以做三件事
a. 輸入的信息是有效的當事人,驗證通過,返回Authentication
b. 輸入的信息是無效的當事人,驗證不通過,返回AuthenticationException
c. 無法判斷的時候,返回一個null
看到這兒,就覺得我想通過查詢 用戶名 = 輸入的用戶名 且 密碼 =輸入密碼的想法真是異想天開了。
Filter Chains,過濾鏈,默認對所有的范文url進行過濾,意味着打開這個網站的任何鏈接,都彈出授權頁面
而如果像如下的例子,則可以在foo下的下url不進行授權驗證,說白了,不登錄,這個url下也可以訪問。 想想日常使用場景,比如總要有個注冊頁面吧,不能全面鏈接都要求登錄。不注冊如何登錄呢?
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
...;
}
}
用戶登錄了之后,要怎么查看個人信息,傳入@AuthenticationPrincipal,當事人對象Principal principal
@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
... // do stuff with user
}
@RequestMapping("/foo")
public String foo(Principal principal) {
Authentication authentication = (Authentication) principal;
User = (User) authentication.getPrincipal();
... // do stuff with user
}
使用規則介紹完了,花了兩天把授權一個可用的項目的代碼整理出來,貼上github路徑
https://github.com/JasmineQian/buglist
其中用的是springboot 2.1.2 Realease 版本,和以前的版本稍微有一點區別,比如必須對密碼加密校驗,So存進去的密碼處,必須加密之后存入數據庫
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
程序中驗證的用戶名和密碼
自己建一張表,叫做qa_user,添加如下數據
2 admin $2a$10$A4EZrzoXqj4mVyXiw/fsp.mJ.Ne5aVAMWrMK0mAb2zY7lJ/H6Jryi admin ROLE_USER,ROLE_ADMIN