SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)url攔截(基于rbac模型)
后續(xù)會(huì)講解如何實(shí)現(xiàn)方法攔截。其實(shí)與url攔截大同小異。
攔截方法,會(huì)更簡(jiǎn)單一點(diǎn)吧 基于PermissionEvaluator 可以自行先了解
1、了解主要的過濾器
1、SecurityMetadataSource
權(quán)限資源攔截器。
有一個(gè)接口繼承與它FilterInvocationSecurityMetadataSource,但FilterInvocationSecurityMetadataSource只是一個(gè)標(biāo)識(shí)接口,
對(duì)應(yīng)于FilterInvocation,本身并無任何內(nèi)容:
主要方法:
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { }
每一次請(qǐng)求url,都會(huì)調(diào)用這個(gè)方法。object存儲(chǔ)了請(qǐng)求的信息。如;rul
2、UserDetailsService
用戶登錄,會(huì)先調(diào)用這里面的 loadUserByUsername。通過用戶名去查詢用戶是否存在數(shù)據(jù)庫。
在這里面進(jìn)行查詢,獲得用戶權(quán)限信息
3、AccessDecisionManager
里面的decide方法。
// decide 方法是判定是否擁有權(quán)限的決策方法, //authentication 是釋UserService中循環(huán)添加到 GrantedAuthority 對(duì)象中的權(quán)限信息集合. //object 包含客戶端發(fā)起的請(qǐng)求的requset信息 ,可轉(zhuǎn)換為 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); //configAttributes 為MyInvocationSecurityMetadataSource的getAttributes(Object object)這個(gè)方法返回的結(jié)果, 此方法是為了判定用戶請(qǐng)求的url 是否在權(quán)限表中,如果在權(quán)限表中,則返回給 decide 方法, 用來判定用戶是否有此權(quán)限。如果不在權(quán)限表中則放行。 @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes)
2、正式實(shí)戰(zhàn)了
1 使用idea的Srping Initializr 創(chuàng)建一個(gè)項(xiàng)目 我的版本如下Pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.source>1.8</maven.compiler.source> <mybatis.version>3.2.7</mybatis.version> <mybatis-spring.version>1.2.2</mybatis-spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</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-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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> <!--提供security相關(guān)標(biāo)簽,可選可不選--> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency> <!--bootstrap組件,可選可不選--> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>3.3.7</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <!--mybatis--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2,創(chuàng)建一個(gè)springSecurity配置類,你也可以使用配置文件的方法。我這里使用了boot的配置類
package com.example.config; import com.example.service.CustomUserService; import com.example.service.MyFilterSecurityInterceptor; import org.springframework.beans.factory.annotation.Autowired; 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.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.core.userdetails.UserDetailsService; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; @Configuration //聲明為配置類 @EnableWebSecurity //這里啟動(dòng)security public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired //下面會(huì)編寫這個(gè)類 private MyFilterSecurityInterceptor myFilterSecurityInterceptor; @Autowired //下面會(huì)編寫這個(gè)類 private DefaultAccessDeniedHandler defaultAccessDeniedHandler; @Bean UserDetailsService customUserService(){ //注冊(cè)UserDetailsService 的bean return new CustomUserService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService()); //user Details Service驗(yàn)證 } @Override protected void configure(HttpSecurity http) throws Exception { http.exceptionHandling() .accessDeniedHandler(defaultAccessDeniedHandler); http.authorizeRequests() .antMatchers("/css/**").permitAll() .anyRequest().authenticated() //任何請(qǐng)求,登錄后可以訪問 .and() .formLogin().loginPage("/login").failureUrl("/login?error").permitAll() //登錄頁面用戶任意訪問 .and() .logout().permitAll(); //注銷行為任意訪問 http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class); } }
3、自定義SecurityMetadataSource攔截器
package com.example.service; import com.example.dao.PermissionDao; import com.example.domain.Permission; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.util.*; /** */ @Service public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { private HashMap<String, Collection<ConfigAttribute>> map =null; @Autowired private PermissionDao permissionDao; /** * 自定義方法。最好在項(xiàng)目啟動(dòng)時(shí),去數(shù)據(jù)庫查詢一次就好。 * 數(shù)據(jù)庫查詢一次 權(quán)限表出現(xiàn)的所有要攔截的url */ public void loadResourceDefine(){ map = new HashMap<>(); Collection<ConfigAttribute> array; ConfigAttribute cfg; //去數(shù)據(jù)庫查詢 使用dao層。 你使用自己的即可 List<Permission> permissions = permissionDao.findAll(); for(Permission permission : permissions) { array = new ArrayList<>(); //下面你可以添加你想要比較的信息過去。 注意的是,需要在用戶登錄時(shí)存儲(chǔ)的權(quán)限信息一致 cfg = new SecurityConfig(permission.getName()); //此處添加了資源菜單的名字,例如請(qǐng)求方法到ConfigAttribute的集合中去。此處添加的信息將會(huì)作為MyAccessDecisionManager類的decide的第三個(gè)參數(shù)。 array.add(cfg); //用權(quán)限的getUrl() 作為map的key,用ConfigAttribute的集合作為 value, map.put(permission.getUrl(), array); } } //此方法是為了判定用戶請(qǐng)求的url 是否在權(quán)限表中,如果在權(quán)限表中,則返回給 decide 方法,用來判定用戶是否有此權(quán)限。如果不在權(quán)限表中則放行。 @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { FilterInvocation filterInvocation = (FilterInvocation) object; String fullRequestUrl = filterInvocation.getFullRequestUrl(); //若是靜態(tài)資源 不做攔截 下面寫了單獨(dú)判斷靜態(tài)資源方法 if (isMatcherAllowedRequest(filterInvocation)) { System.out.println("我沒有被攔截"+fullRequestUrl); return null; } //map 為null 就去數(shù)據(jù)庫查 if(map ==null) { loadResourceDefine(); } //測(cè)試 先每次都查 //object 中包含用戶請(qǐng)求的request 信息 HttpServletRequest request = filterInvocation.getHttpRequest(); AntPathRequestMatcher matcher; String resUrl; for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) { resUrl = iter.next(); matcher = new AntPathRequestMatcher(resUrl); if(matcher.matches(request)) { return map.get(resUrl); } } return null; } /** * 判斷當(dāng)前請(qǐng)求是否在允許請(qǐng)求的范圍內(nèi) * @param fi 當(dāng)前請(qǐng)求 * @return 是否在范圍中 */ private boolean isMatcherAllowedRequest(FilterInvocation fi){ return allowedRequest().stream().map(AntPathRequestMatcher::new) .filter(requestMatcher -> requestMatcher.matches(fi.getHttpRequest())) .toArray().length > 0; } /** * @return 定義允許請(qǐng)求的列表 */ private List<String> allowedRequest(){ return Arrays.asList("/login","/css/**","/fonts/**","/js/**","/scss/**","/img/**"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return true; } }
自定義UserDetailsService 。登錄的時(shí)候根據(jù)用戶名去數(shù)據(jù)庫查詢用戶擁有的權(quán)限信息
package com.example.service; import com.example.dao.PermissionDao; import com.example.dao.UserDao; import com.example.domain.Permission; import com.example.domain.SysRole; import com.example.domain.SysUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * Created by yangyibo on 17/1/18. */ @Service public class CustomUserService implements UserDetailsService { //自定義UserDetailsService 接口 @Autowired UserDao userDao; @Autowired PermissionDao permissionDao; public UserDetails loadUserByUsername(String username) { SysUser user = userDao.findByUserName(username); for (SysRole role : user.getRoles()) { System.out.println(role.getName()); } if (user != null) { //根據(jù)用戶id 去查找用戶擁有的資源 List<Permission> permissions = permissionDao.findByAdminUserId(user.getId()); List<GrantedAuthority> grantedAuthorities = new ArrayList <>(); for (Permission permission : permissions) { if (permission != null && permission.getName()!=null) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName()); //1:此處將權(quán)限信息添加到 GrantedAuthority 對(duì)象中,在后面進(jìn)行全權(quán)限驗(yàn)證時(shí)會(huì)使用GrantedAuthority 對(duì)象。 grantedAuthorities.add(grantedAuthority); } } return new User(user.getUsername(), user.getPassword(), grantedAuthorities); } else { throw new UsernameNotFoundException("admin: " + username + " do not exist!"); } } }
自定義AccessDecisionManager
package com.example.service; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; import java.util.Collection; import java.util.Iterator; @Service public class MyAccessDecisionManager implements AccessDecisionManager { // decide 方法是判定是否擁有權(quán)限的決策方法, //authentication 是釋CustomUserService中循環(huán)添加到 GrantedAuthority 對(duì)象中的權(quán)限信息集合. //object 包含客戶端發(fā)起的請(qǐng)求的requset信息,可轉(zhuǎn)換為 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); //configAttributes 為MyInvocationSecurityMetadataSource的getAttributes(Object object)這個(gè)方法返回的結(jié)果,此方法是為了判定用戶請(qǐng)求的url 是否在權(quán)限表中,如果在權(quán)限表中,則返回給 decide 方法,用來判定用戶是否有此權(quán)限。如果不在權(quán)限表中則放行。 @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if (null == configAttributes || configAttributes.size() <= 0 ) { return; } ConfigAttribute c; String needRole; for (Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) { c = iter.next(); needRole = c.getAttribute(); for (GrantedAuthority ga : authentication.getAuthorities()) { //authentication 為在注釋1 中循環(huán)添加到 GrantedAuthority 對(duì)象中的權(quán)限信息集合 if (needRole.trim().equals(ga.getAuthority())) { return; } } } throw new AccessDeniedException("no right"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class<?> aClass) { return true; } }
自定義攔截器
package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.SecurityMetadataSource; import org.springframework.security.access.intercept.AbstractSecurityInterceptor; import org.springframework.security.access.intercept.InterceptorStatusToken; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.stereotype.Service; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; @Service public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { @Autowired private FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowired private void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) { super.setAccessDecisionManager(myAccessDecisionManager); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain); invoke(fi); } public void invoke(FilterInvocation fi) throws IOException, ServletException { //fi里面有一個(gè)被攔截的url //里面調(diào)用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個(gè)方法獲取fi對(duì)應(yīng)的所有權(quán)限 //再調(diào)用MyAccessDecisionManager的decide方法來校驗(yàn)用戶的權(quán)限是否足夠 InterceptorStatusToken token = super.beforeInvocation(fi); try { //執(zhí)行下一個(gè)攔截器 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } @Override public void destroy() { } @Override public Class<?> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } }
運(yùn)行項(xiàng)目就實(shí)現(xiàn)了。去試試吧。
記得將自定義攔截器放進(jìn)security的過濾器鏈中。
到此這篇關(guān)于SpringSecurity實(shí)現(xiàn)動(dòng)態(tài)url攔截(基于rbac模型)的文章就介紹到這了,更多相關(guān)SpringSecurity 動(dòng)態(tài)url攔截內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java的GUI編程之列表和組合框的設(shè)計(jì)使用
這篇文章主要介紹了Java的GUI編程之列表和組合框的設(shè)計(jì)使用,是Java圖形界面編程中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10在idea中將創(chuàng)建的java web項(xiàng)目部署到Tomcat中的過程圖文詳解
這篇文章主要介紹了在idea中將創(chuàng)建的java web項(xiàng)目部署到Tomcat中的過程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Open-Feign整合hystrix降級(jí)熔斷實(shí)戰(zhàn)記錄
這篇文章主要介紹了Open-Feign整合hystrix降級(jí)熔斷實(shí)戰(zhàn)記錄,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Java設(shè)計(jì)模式之靜態(tài)代理模式實(shí)例分析
這篇文章主要介紹了Java設(shè)計(jì)模式之靜態(tài)代理模式,結(jié)合實(shí)例形式分析了靜態(tài)代理模式的概念、原理、定義與用法,需要的朋友可以參考下2018-04-04調(diào)用Process.waitfor導(dǎo)致的進(jìn)程掛起問題及解決
這篇文章主要介紹了調(diào)用Process.waitfor導(dǎo)致的進(jìn)程掛起問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12