javascript
SpringSecurity授权(访问控制)
一、 訪問控制url匹配
在前面講解了認(rèn)證中所有常用配置,主要是對httpSecurity.formLogin()進行操作。而在配置類中httphttpSecurity.authorizeRequests()主要是對url進行控制,也就是我們所說的授權(quán)(訪問控制)。httpSecurity.authorizeRequests()也支持連綴寫法,可以有很多url匹配規(guī)則和很多權(quán)限控制方法。這些內(nèi)容進行各種組合就形成了Spring Security中的授權(quán)。
在所有匹配規(guī)則中取所有規(guī)則的交集。配置順序影響了之后授權(quán)效果,越是具體的應(yīng)該放在前面,越是籠統(tǒng)的應(yīng)該放到后面。
1 antMatcher()
規(guī)則如下: ? 匹配一個字符 * 匹配0個或多個字符 ** 匹配0個或多個目錄 在實際項目中經(jīng)常需要放行所有靜態(tài)資源,下面演示放行js文件夾下所有腳本文件。 .antMatchers("/js/**").permitAll() 還有一種配置方式是只要是.js文件都放行 antMatchers("/**/*.js").permitAll()2 anyRequest()
在之前認(rèn)證過程中我們就已經(jīng)使用過anyRequest(),表示匹配所有的請求。一般情況下此方法都會使用,設(shè)置全部內(nèi)容都需要進行認(rèn)證。 代碼示例: anyRequest().authenticated();3 regexMatchers()
3.1 介紹
使用正則表達式進行匹配。和antMatchers()主要的區(qū)別就是參數(shù),antMatchers()參數(shù)是ant表達式,regexMatchers()參數(shù)是正則表達式。
演示所有以.js結(jié)尾的文件都被放行。
.regexMatchers(".+[.]js").permitAll()
3.2 兩個參數(shù)時使用方式
無論是antMatchers()還是regexMatchers()都具有兩個參數(shù)的方法,其中第一個參數(shù)都是HttpMethod,表示請求方式,當(dāng)設(shè)置了HttpMethod后表示只有設(shè)定的特定的請求方式才執(zhí)行對應(yīng)的權(quán)限設(shè)置。
二、 內(nèi)置訪問控制方法介紹
Spring Security匹配了URL后調(diào)用了permitAll()表示不需要認(rèn)證,隨意訪問。在Spring Security中提供了多種內(nèi)置控制。
底層都是基于access進行實現(xiàn)的。
1 permitAll()
permitAll()表示所匹配的URL任何人都允許訪問。
2 authenticated()
authenticated()表示所匹配的URL都需要被認(rèn)證才能訪問。
3 anonymous()
anonymous()表示可以匿名訪問匹配的URL。和permitAll()效果類似,只是設(shè)置為anonymous()的url會執(zhí)行filter 鏈中
4 denyAll()
denyAll()表示所匹配的URL都不允許被訪問。
如果用戶未被認(rèn)證需要認(rèn)證,如果已經(jīng)認(rèn)證,報403
5 rememberMe()
被“remember me”的用戶允許訪問
6 fullyAuthenticated()
如果用戶不是被remember me的,才可以訪問。
三、 角色權(quán)限判斷
除了之前講解的內(nèi)置權(quán)限控制。Spring Security中還支持很多其他權(quán)限控制。這些方法一般都用于用戶已經(jīng)被認(rèn)證后,判斷用戶是否具有特定的要求。
底層也是調(diào)用access(參數(shù))。參數(shù)正好是我們調(diào)用的方法名。注意如果是判斷角色會給調(diào)用方法參數(shù)前面添加ROLE_,這也是為什么正常調(diào)用方法時角色不允許以ROLE_的原因。
1 hasAuthority(String)
判斷用戶是否具有特定的權(quán)限,用戶的權(quán)限是在自定義登錄邏輯中創(chuàng)建User對象時指定的。
在配置類中通過hasAuthority(“admin”)設(shè)置具有admin權(quán)限時才能訪問。
.antMatchers("/main1.html").hasAuthority("admin")1.1 數(shù)據(jù)庫代碼效果演示
1.1.1 編寫mapper
根據(jù)用戶名查詢用戶權(quán)限
/*** 根據(jù)用戶名查詢用戶權(quán)限* @param username* @return*/ List<String> selectPermissionByUsername(String username); <select id="selectPermissionByUsername" parameterType="string" resultType="string">select m.permission from user u,role_user ru,role r,role_menu rm,menu m where ru.uid=u.id and u.username=#{param1} and ru.rid=r.id and rm.rid=r.id and rm.mid=m.id </select>1.1.2 修改MyUserDetailsServiceImpl 查詢用戶對應(yīng)的權(quán)限
com.bjsxt.springscuritydemo.service.impl.MyUserDetailsServiceImpl
@Service public class MyUserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userMapper.selectByUsername(username);if(user==null){throw new UsernameNotFoundException("用戶名不存在");}// 查詢用戶對應(yīng)的權(quán)限List<String> listPermission = userMapper.selectPermissionByUsername(username);List<SimpleGrantedAuthority> listAuthority = new ArrayList<SimpleGrantedAuthority>();for(String permisssion : listPermission){listAuthority.add(new SimpleGrantedAuthority(permisssion));}return new org.springframework.security.core.userdetails.User(username,user.getPassword(),listAuthority);} }1.1.3 在配置類中添加
httpSecurity.authorizeRequests()// 放行 url / 。不放行死循環(huán).antMatchers("/").permitAll().antMatchers("/fail").permitAll().antMatchers("/bjsxt").hasAuthority("menu:sys").anyRequest().authenticated();1.1.4 測試效果
在瀏覽器中輸入 http://localhost:8080/bjsxt 會要求進行登錄認(rèn)證,認(rèn)證后如果用戶具有menu:sys權(quán)限會正常控制器,如果沒有權(quán)限會報403
2 hasAnyAuthority(String …)
如果用戶具備給定權(quán)限中某一個,就允許訪問。
下面代碼中由于大小寫和用戶的權(quán)限不相同,所以用戶無權(quán)訪問/main1.html
.antMatchers("/main1.html").hasAnyAuthority(“adMin”,“admiN”)
3 hasRole(String)
如果用戶具備給定角色就允許訪問。否則出現(xiàn)403。
參數(shù)取值來源于自定義登錄邏輯UserDetailsService實現(xiàn)類中創(chuàng)建User對象時給User賦予的授權(quán)。
在給用戶賦予角色時角色需要以:ROLE_ 開頭,后面添加角色名稱。例如:ROLE_abc 其中abc是角色名,ROLE_是固定的字符開頭。使用hasRole()時參數(shù)也只寫abc即可。否則啟動報錯。
給用戶賦予角色:
在配置類中直接寫abc即可。
3.1 數(shù)據(jù)庫操作
3.1.1 編寫mapper
在接口中添加com.bjsxt.springscuritydemo.mapper.UserMapper
3.1.2 修改自定義登錄邏輯
com.bjsxt.springscuritydemo.service.impl.MyUserDetailsServiceImpl
3.1.3 在配置類中添加
.antMatchers("/bjsxt").hasRole("管理員")3.1.4 測試
發(fā)現(xiàn)使用張三可以進行登錄,李四訪問時403
4 hasAnyRole(String …)
如果用戶具備給定角色的任意一個,就允許被訪問
5 hasIpAddress(String)
如果請求是指定的IP就運行訪問。
可以通過request.getRemoteAddr()獲取ip地址。
需要注意的是在本機進行測試時localhost和127.0.0.1輸出的ip地址是不一樣的。
當(dāng)瀏覽器中通過localhost進行訪問時控制臺打印的內(nèi)容:
當(dāng)瀏覽器中通過127.0.0.1訪問時控制臺打印的內(nèi)容:
當(dāng)瀏覽器中通過具體ip進行訪問時控制臺打印內(nèi)容:
四、 自定義403處理方案
使用Spring Security時經(jīng)常會看見403(無權(quán)限),默認(rèn)情況下顯示的效果如下:
而在實際項目中可能都是一個異步請求,顯示上述效果對于用戶就不是特別友好了。Spring Security支持自定義權(quán)限受限。
1 新建類
新建類實現(xiàn)AccessDeniedHandler。
1 修改配置類
配置類中重點添加異常處理器。設(shè)置訪問受限后交給哪個對象進行處理。
myAccessDeniedHandler是在配置類中進行自動注入的。
在configure()方法中添加下面內(nèi)容
//異常處理 httpSecutity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);五、 基于表達式的訪問控制
1 access()方法使用
之前學(xué)習(xí)的登錄用戶權(quán)限判斷實際上底層實現(xiàn)都是調(diào)用access(表達式)
可以通過access()實現(xiàn)和之前學(xué)習(xí)的權(quán)限控制完成相同的功能。
1.1 以permitAll舉例
下面兩種寫法是等效
1.2 以hasRole舉例,下面兩種寫法是等效的。
注意:
hasRole() 參數(shù)是必須不能以ROLE_開頭
access中hasRole一定要以ROLE_開頭
六、 基于注解的訪問控制
在Spring Security中提供了一些訪問控制的注解。這些注解都是默認(rèn)是都不可用的,需要通過@EnableGlobalMethodSecurity進行開啟后使用。
如果設(shè)置的條件允許,程序正常執(zhí)行。如果不允許會報500
這些注解可以寫到Service接口或方法上上也可以寫到Controller或Controller的方法上。通常情況下都是寫在控制器方法上的,控制接口URL是否允許被訪問。
1 @Secured
@Secured是專門用于判斷是否具有角色的。能寫在方法或類上。@Secured參數(shù)要以ROLE_開頭。
1.1 實現(xiàn)步驟
1.1.1 開啟注解
在啟動類(也可以在配置類等能夠掃描的類上)上添加@EnableGlobalMethodSecurity(securedEnabled = true)
1.1.2 在控制器方法上添加@Secured注解
在LoginController中方法上添加注解
1.1.3 配置類
配置類中方法配置保留最基本的配置即可。
2 @PreAuthorize/@PostAuthorize
@PreAuthorize和@PostAuthorize都是方法或類級別注解。
@PreAuthorize表示訪問方法或類在執(zhí)行之前先判斷權(quán)限,大多情況下都是使用這個注解,注解的參數(shù)和access()方法參數(shù)取值相同,都是權(quán)限表達式。
@PostAuthorize表示方法或類執(zhí)行結(jié)束后判斷權(quán)限,此注解很少被使用到。
注意:
必須在啟動類@EnableGlobalMethodSecurity中設(shè)置prePostEnabled = true
2.1 實現(xiàn)步驟
2.1.1 開啟注解
在啟動類中開啟@PreAuthorize注解。
2.1.2 @PreAuthorize演示
在控制器方法上添加@PreAuthorize,參數(shù)可以是任何access()支持的表達式。
如果用戶沒有管理員角色,不會打印preAuthorize
2.1.3 @PostAuthorize演示
如果用戶沒有管理員角色也會打印PostAuthorize
七、 Remember Me功能實現(xiàn)
Spring Security 中Remember Me為“記住我”功能,用戶只需要在登錄時添加remember-me復(fù)選框,取值為true。Spring Security會自動把用戶信息存儲到數(shù)據(jù)源中,以后就可以不登錄進行訪問。
1 添加依賴
Spring Security實現(xiàn)Remember Me 功能時底層實現(xiàn)依賴Spring-JDBC,所以需要導(dǎo)入Spring-JDBC。以后多使用MyBatis框架而很少直接導(dǎo)入spring-jdbc,所以此處導(dǎo)入mybatis啟動器
同時還需要添加MySQL驅(qū)動
2 配置數(shù)據(jù)源
在application.yml中配置數(shù)據(jù)源。請確保數(shù)據(jù)庫中已經(jīng)存在security數(shù)據(jù)庫
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/securityusername: rootpassword: 12343 編寫配置
新建com.bjsxt.config.RememberMeConfig類,并創(chuàng)建Bean對象
@Configuration public class RememberMeConfig {@Autowiredprivate DataSource dataSource;@Beanpublic PersistentTokenRepository getPersistentTokenRepository() {JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl=new JdbcTokenRepositoryImpl();jdbcTokenRepositoryImpl.setDataSource(dataSource);//自動建表,第一次啟動時需要,第二次啟動時注釋掉 // jdbcTokenRepositoryImpl.setCreateTableOnStartup(true);return jdbcTokenRepositoryImpl;} }4 修改SecurityConfig
在SecurityConfig中添加RememberMeConfig和UserDetailsService實現(xiàn)類對象,并自動注入。
在configure中添加下面配置內(nèi)容。
5 在客戶端頁面中添加復(fù)選框
在客戶端登錄頁面中添加remember-me的復(fù)選框,只要用戶勾選了復(fù)選框下次就不需要進行登錄了。
<form action = "/login" method="post">用戶名:<input type="text" name="username"/><br/>密碼:<input type="text" name="password"/><br/><input type="checkbox" name="remember-me" value="true"/> <br/><input type="submit" value="登錄"/> </form>6 有效時間
默認(rèn)2周時間。但是可以通過設(shè)置狀態(tài)有效時間,即使項目重新啟動下次也可以正常登錄。
//remember Me http.rememberMe().tokenValiditySeconds(120)//單位:秒.tokenRepository(repository).userDetailsService(userDetailsServiceImpl);八、 Thymeleaf中Spring Security的使用
Spring Security可以在一些視圖技術(shù)中進行控制顯示效果。例如:JSP或Thymeleaf。在非前后端分離且使用Spring Boot的項目中多使用Thymeleaf作為視圖展示技術(shù)。
Thymeleaf對Spring Security的支持都放在thymeleaf-extras-springsecurityX中。所以需要在項目中添加此jar包的依賴和thymeleaf的依賴。
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId><version>3.0.4.RELEASE</version> </dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>在html頁面中引入thymeleaf命名空間和security命名空間
<html xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">1 獲取屬性
可以在html頁面中通過 sec:authentication=""獲取UsernamePasswordAuthenticationToken中所有g(shù)etXXX的內(nèi)容,包含父類中的getXXX的內(nèi)容。
根據(jù)源碼得出下面屬性:
? name:登錄賬號名稱
? principal:登錄主體,在自定義登錄邏輯中是UserDetails
? credentials:憑證
? authorities:權(quán)限和角色
? details:實際上是WebAuthenticationDetails的實例。可以獲取remoteAddress(客戶端ip)和sessionId(當(dāng)前sessionId)
1.1 實現(xiàn)步驟:
1.1.1 新建demo.html
在項目resources中新建templates文件夾,在templates中新建demo.html頁面
1.1.2 編寫demo.html
在demo.html中編寫下面內(nèi)容,測試獲取到的值
1.1.3 編寫控制器
thymeleaf頁面需要控制轉(zhuǎn)發(fā),在控制器類中編寫下面方法
2 權(quán)限判斷
在html頁面中可以使用sec:authorize=”表達式”進行權(quán)限控制,判斷是否顯示某些內(nèi)容。表達式的內(nèi)容和access(表達式)的用法相同。如果用戶具有指定的權(quán)限,則顯示對應(yīng)的內(nèi)容;如果表達式不成立,則不顯示對應(yīng)的元素。
2.1 不同權(quán)限的用戶顯示不同的按鈕
2.1.1 設(shè)置用戶角色和權(quán)限
設(shè)定用戶具有admin,/insert,/delete權(quán)限ROLE_abc角色。
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_abc,/insert,/delete"));2.1.2 控制頁面顯示效果
在頁面中根據(jù)用戶權(quán)限和角色判斷頁面中顯示的內(nèi)容
九、 退出登錄
用戶只需要向Spring Security項目中發(fā)送/logout退出請求即可。
<a href="/logout">退出登錄</a>為了實現(xiàn)更好的效果,通常添加退出的配置。默認(rèn)的退出url為/logout,退出成功后跳轉(zhuǎn)到/login?logout
如果不希望使用默認(rèn)值,可以通過下面的方法進行修改。
httpSecurity.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html");十、 Spring Security中CSRF
從剛開始學(xué)習(xí)Spring Security時,在配置類中一直存在這樣一行代碼:httpSecurity.csrf().disable();如果沒有這行代碼導(dǎo)致用戶無法被認(rèn)證。
這行代碼的含義是:關(guān)閉csrf防護。
1 什么是CSRF
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack” 或者Session Riding。通過偽造用戶請求訪問受信任站點的非法請求訪問。
跨域:只要網(wǎng)絡(luò)協(xié)議,ip地址,端口中任何一個不相同就是跨域請求。
客戶端與服務(wù)進行交互時,由于http協(xié)議本身是無狀態(tài)協(xié)議,所以引入了cookie進行記錄客戶端身份。在cookie中會存放session id用來識別客戶端身份的。在跨域的情況下,session id可能被第三方惡意劫持,通過這個session id向服務(wù)端發(fā)起請求時,服務(wù)端會認(rèn)為這個請求是合法的,可能發(fā)生很多意想不到的事情。
2 Spring Security中CSRF
從Spring Security4開始CSRF防護默認(rèn)開啟。默認(rèn)會攔截請求。進行CSRF處理。CSRF為了保證不是其他第三方網(wǎng)站訪問,要求訪問時攜帶參數(shù)名為_csrf值為token(token在服務(wù)端產(chǎn)生)的內(nèi)容,如果token和服務(wù)端的token匹配成功,則正常訪問。
2.1 實現(xiàn)步驟
2.1.1 編寫控制器方法
編寫控制器方法,跳轉(zhuǎn)到templates中l(wèi)ogin.html頁面。
2.1.2 新建login.html
在項目resources下新建templates文件夾,并在文件夾中新建login.html頁面。
2.1.3 修改配置類
在配置類中注釋掉CSRF防護失效
2.2 Spring Security中CSRF原理
總結(jié)
以上是生活随笔為你收集整理的SpringSecurity授权(访问控制)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringSecurity认证
- 下一篇: SpringCloud Netflix