本覺得是總結,最後寫成了筆記,因此仍是須要更加的努力啊。
html
開始的時候看了一遍官方文檔,而後只看懂了加密器。java
而後又學了一個尚硅谷的視頻,雖然這個教程是在講一個項目,但我沒有聽懂(應該是我本身的問題)mysql
代碼 https://gitee.com/pilearn/learning-spring-security
中文版文檔 https://www.springcloud.cc/spring-security.html
尚硅谷視頻連接 https://www.bilibili.com/video/BV15a411A7kPgit
Security是Spring全家桶中一個安全框架,他的擴展能力很是的強,底層是一條過濾器鏈。經過簡單的配置就可使用,但經過本身的DIY,能夠把每一個權限細化到每一個連接上去。web
shiro沒有學,但只推薦學一個安全框架
redis
這裏搭建的學習項目都是使用SpringBootspring
你能夠在maven官網獲取最新版本sql
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.4.2</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.pipihao</groupId> <artifactId>securitylearn</artifactId> <version>0.0.1-SNAPSHOT</version> <name>securitylearn</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <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> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
server: port: 8001 spring: datasource: url: jdbc:mysql://localhost:3306/demo?serverTimezone=Asia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false # 由於Thymeleaf不少有默認配置,因此只關了這個緩存,方便刷新
數據庫版本爲 8.0數據庫
用戶名:userapache
密碼:控制檯輸出的這密碼
spring: security: user: name: xx password: xx
WebSecurityConfigurerAdapter 類是是Security內置提供了一個默認身份驗證的抽象類,繼承此抽象類實現configure方法則能夠對驗證操做實現DIY。[於官方文檔 6.3 標題可見]
UserDetailsService接口:查詢數據庫用戶名和密碼過程
@Override protected void configure(HttpSecurity http) throws Exception { /* 使用and()方法表示關閉XML標記的Java配置,它容許咱們繼續配置父標記。若是您閱讀代碼,它也是有道理的。我想配置受權請求並配置表單登陸並配置HTTP基自己份驗證。 */ http .authorizeRequests() .antMatchers("/","/no").permitAll() //能夠直接訪問的路徑 .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") //配置登陸路徑 .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/hallo") .permitAll() ; //設置 登陸的網頁 http.csrf().disable(); //若是註釋了這一行,所有要用_csrf的對象來驗證了 }
若是是配置訪問角色則使用是hasRole與hasAnyRole
這裏很是建議點一下看一下hasRole的源碼 使用Role的時候,User的權限列表是須要加ROLE_前綴的
這裏直接使用的是hasAnyAuthority,還有一個方法是hasAuthority
前者能夠配置多個權限,然後者只能配置一個權限
接口只是顯示一個字符串
@GetMapping("test") public String sayTest(){ return "Test"; }
@Override protected void configure(HttpSecurity http) throws Exception { /* 使用and()方法表示關閉XML標記的Java配置,它容許咱們繼續配置父標記。若是您閱讀代碼,它也是有道理的。我想配置受權請求並配置表單登陸並配置HTTP基自己份驗證。 */ http .authorizeRequests() .antMatchers("/","/no").permitAll() //能夠直接訪問的路徑 .antMatchers("/test").hasAnyAuthority("admin") // 訪問權限 .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") //配置登陸路徑 .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/hallo") .permitAll() ; //設置 登陸的網頁 http.csrf().disable(); //若是註釋了這一行,所有要用_csrf的對象來驗證了 }
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if(StringUtils.isEmpty(username)){ throw new RuntimeException("用戶名不能爲空"); } IUser iUser= userMapper.getUserByUsername(username); if(iUser == null){ throw new UsernameNotFoundException("無此用戶"); } /*此處查詢用戶角色*/ List<GrantedAuthority> grantedAuthorityList = AuthorityUtils.createAuthorityList("admin"); // 權限的列表 return new User(iUser.getUsername(),bCryptPasswordEncoder.encode(iUser.getPassword()),grantedAuthorityList); }
// 在此方法內加上一行 protected void configure(HttpSecurity http) http.exceptionHandling().accessDeniedPage("/unauth.html");
判斷是否有角色,這裏匹配的角色須要加前綴ROLE_
@GetMapping("update") @Secured({"ROLE_manager"}) public String update(){ return "update"; }
使用其功能時須要在application類上開起
@SpringBootApplication @MapperScan("com.pipihao.securitylearn.mapper") @EnableGlobalMethodSecurity(securedEnabled = true) public class SecuritylearnApplication { public static void main(String[] args) { SpringApplication.run(SecuritylearnApplication.class, args); } }
UserDetailsServiceImpl
List<GrantedAuthority> grantedAuthorityList = AuthorityUtils.createAuthorityList("admin","ROLE_manager");
此註解即有權限驗證功能,又有角色驗證功能
@GetMapping("pre1") @PreAuthorize("hasAnyRole('ROLE_manager')") public String prePost1(){ return "prePost1"; } @GetMapping("pre2") @PreAuthorize("hasAnyAuthority('admin')") public String prePost2(){ return "prePost2"; }
@SpringBootApplication @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecuritylearnApplication { public static void main(String[] args) { SpringApplication.run(SecuritylearnApplication.class, args); } }
@PostAuthorize 與@PreAuthorize的區別就是,Pre會先攔截後執行,而PostAuthorize是先執行,後攔截
因此我例子中沒有過多的講
Pre是過濾上傳的數據,Post過濾返回的數據
@GetMapping("list") @PostFilter("filterObject.username != 'admin' ") public List<IUser> list(){ List<IUser> iUsers = new ArrayList<>(); iUsers.add(new IUser(1,"admin","123")); iUsers.add(new IUser(2,"user","123")); return iUsers; } // Applicationo類上仍是要加上下面這個註解,並設置屬性值 @EnableGlobalMethodSecurity(prePostEnabled = true)
效果圖
上傳則是同理,經過註解寫好判斷,而後測試便可,注:PreFilter過濾的也只是集合和數組
/*配置退出登陸*/ http.logout().logoutUrl("/logout").logoutSuccessUrl("no").permitAll();
登陸後,直接經過瀏覽器,訪問此路徑便可(是的,就是如此)
location.href='/logout';
下面是尚硅谷老師寫的原理圖和執行流程
若是是微服務,則把數據庫改爲redis,把cookie改爲jwt生成的token
Security 中的一個類內JdbcTokenRepositoryImpl
的常量CREATE_TABLE_SQL
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)
有興趣的能夠看看源碼 沒興趣的直接在你使用的數據庫內執行上面這行sql建立一個保存登陸信息的表
JdbcTokenRepositoryImpl 是PersistentTokenRepository實現類
下面這種寫那麼應該是多態了
@Autowired private DataSource dataSource; @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); //jdbcTokenRepository.setCreateTableOnStartup(true); 設置啓動時建立自動登陸表 return jdbcTokenRepository; }
SecurityConfig的方法
@Override protected void configure(HttpSecurity http) throws Exception { /*自定義403連接*/ http.exceptionHandling().accessDeniedPage("/unauth.html"); /*配置退出登陸*/ http.logout().logoutUrl("/logout").logoutSuccessUrl("/no").permitAll(); /* 使用and()方法表示關閉XML標記的Java配置,它容許咱們繼續配置父標記。若是您閱讀代碼,它也是有道理的。我想配置受權請求並配置表單登陸並配置HTTP基自己份驗證。 */ http .authorizeRequests() .antMatchers("/","/no").permitAll() //能夠直接訪問的路徑 .antMatchers("/test").hasAnyAuthority("admin") .antMatchers("/unauth").hasAnyAuthority("xxx") .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") //配置登陸路徑 .loginProcessingUrl("/doLogin") .defaultSuccessUrl("/hallo") .permitAll() // -------------------就是下面這坨 .and() .rememberMe().tokenRepository(persistentTokenRepository()) .tokenValiditySeconds(60) // 自動保存的時間,秒爲單位 .userDetailsService(userDetailsService) ; //設置 登陸的網頁 http.csrf().disable(); //若是註釋了這一行,所有要用_csrf的對象來驗證了 }
下面是登陸界面
<form action="/doLogin" method="POST"> user:<input type="text" name="username"><br> pswd:<input type="text" name="password"><br> <!--必須name=remember-me否則,是沒法接收到是否自動登陸的信息的--> 自動登陸 <input type="checkbox" name="remember-me"><br> <input type="submit"> </form>
而後在登陸的時候打個勾,就能夠自動登陸了
在DB中會出現以下的信息
第一步 把下面這一行註釋了就開啓了,也就是說他實際上是默認開啓的
若是沒有關閉,則會NullPointerException
//http.csrf().disable();
Spring Security CSRF 會針對Patch,Post,Put,Delete方法進行防禦。(都是一些要更改數據的方法)
系統默認提供了一個csrfToken對象放在HttpSession中,也就是咱們所見到了_csrf對象
此對象能夠直接使用
開啓CSRF後,則登陸的時【POST】,也須要驗證CSRF,而使用HttpSession則須要使用模板引擎,這裏咱們使用的是Thymeleaf而非JSP。(大同小異)
注:使用Thymeleaf的時候,類上的Controller註解不能寫成RestController,否則沒法生效的
@Controller public class LoginController { @GetMapping("login") public String login(){ return "login"; } }
<!doctype html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>登陸</title> </head> <body> <!--沒加th:則不會有隱藏域自動生成--> <form th:action="'/doLogin'" method="POST"> user:<input type="text" name="username"><br> pswd:<input type="text" name="password"><br> <!--必須name=remember-me否則,是沒法接收到是否自動登陸的信息的--> 自動登陸 <input type="checkbox" name="remember-me"><br> <input type="submit"> </form> </body> </html>
切記,默認開了CSRF,則每一個表單中應當手動添加一個隱藏域
當Thymeleaf由於你使用了th,則自動給你生成了。
因此 th:action="'/doLogin'"
這樣寫能夠省事
以下圖
本是總結,誰知仍是變成了學習筆記。總結表明着會,筆記表明着只能用,說不出什麼名堂。這是看第二遍,固然,這也會像我用正則同樣,每次用正則的時候,都要學一遍正則。
或許SpringSecurity並不難,難的只是步驟有點多。
老師講的很不錯,多聽幾遍就會了。
關於提升技術,應該看文檔,把他提供的API都本身看懂。像用Redist代替DB,這樣的微服務中,使用,頗有效率。
接下來,我還會繼續學習Security,並出些新筆記,這最多算是一個聽課筆記。