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,本身并無(wú)任何內(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ù)庫(kù)。
在這里面進(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() //登錄頁(yè)面用戶任意訪問
.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ù)庫(kù)查詢一次就好。
* 數(shù)據(jù)庫(kù)查詢一次 權(quán)限表出現(xiàn)的所有要攔截的url
*/
public void loadResourceDefine(){
map = new HashMap<>();
Collection<ConfigAttribute> array;
ConfigAttribute cfg;
//去數(shù)據(jù)庫(kù)查詢 使用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ù)庫(kù)查
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ù)庫(kù)查詢用戶擁有的權(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)文章希望大家以后多多支持腳本之家!
- SpringSecurity自定義資源攔截規(guī)則及登錄界面跳轉(zhuǎn)問題
- SpringSecurity攔截器鏈的使用詳解
- springsecurity實(shí)現(xiàn)攔截器的使用示例
- SpringBoot整合SpringSecurity實(shí)現(xiàn)認(rèn)證攔截的教程
- Swagger2不被SpringSecurity框架攔截的配置及說明
- Spring Boot security 默認(rèn)攔截靜態(tài)資源的解決方法
- Spring Security攔截器引起Java CORS跨域失敗的問題及解決
- SpringBoot+SpringSecurity 不攔截靜態(tài)資源的實(shí)現(xiàn)
- 淺談Spring Security 對(duì)于靜態(tài)資源的攔截與放行
- spring Security配置攔截規(guī)則小結(jié)
相關(guān)文章
Java 中的字符串替換方法之replace, replaceAll 和 rep
在Java中,字符串的替換是一種常見的操作,特別是在處理文本和格式化輸出時(shí),本文將詳細(xì)討論這些方法的用法、區(qū)別以及示例,感興趣的朋友一起看看吧2024-12-12
java和javascript中過濾掉img形式的字符串不顯示圖片的方法
這篇文章主要介紹了java和javascript中過濾掉img形式的字符串不顯示圖片的方法,以實(shí)例形式分別講述了采用java和javascript實(shí)現(xiàn)過濾掉img形式字符串的技巧,需要的朋友可以參考下2015-02-02
Java自學(xué)書籍推薦 程序員到架構(gòu)師必看的書
這篇文章主要為大家推薦了Java程序員到架構(gòu)師自學(xué)書籍,幫助大家不斷提高自己的專業(yè)水平,感興趣的小伙伴們可以參考一下2016-09-09
Java C++題解leetcode816模糊坐標(biāo)示例
這篇文章主要為大家介紹了Java C++題解leetcode816模糊坐標(biāo)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
如何自定義hibernate validation注解示例代碼
Hibernate Validator 是 Bean Validation 的參考實(shí)現(xiàn) . Hibernate Validator 提供了 JSR 303 規(guī)范中所有內(nèi)置 constraint 的實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于如何自定義hibernate validation注解的相關(guān)資料,需要的朋友可以參考下2018-04-04
Java 中的vector和list的區(qū)別和使用實(shí)例詳解
在大家還沒有了解vector,list,deque的知識(shí)之前,我先給大家介紹下stl,本文重點(diǎn)給大家介紹vector和list的區(qū)別及使用,感興趣的的朋友一起看看吧2017-09-09

