好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringSecurity实现动态url拦截(基于rbac模型)

后续会讲解如何实现方法拦截。其实与url拦截大同小异。

拦截方法,会更简单一点吧 基于PermissionEvaluator 可以自行先了解

1、了解主要的过滤器

1、SecurityMetadataSource

权限资源拦截器。

有一个接口继承与它FilterInvocationSecurityMetadataSource,但FilterInvocationSecurityMetadataSource只是一个标识接口,
 对应于FilterInvocation,本身并无任何内容:

主要方法:

?

1

2

3

public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

 

}

每一次请求url,都会调用这个方法。object存储了请求的信息。如;rul

2、UserDetailsService

用户登录,会先调用这里面的 loadUserByUsername。通过用户名去查询用户是否存在数据库。

? 在这里面进行查询,获得用户权限信息

3、AccessDecisionManager

里面的decide方法。

?

1

2

3

4

5

6

7

8

9

// decide 方法是判定是否拥有权限的决策方法,

     //authentication 是释UserService中循环添加到 GrantedAuthority 对象中的权限信息集合.

     //object 包含客户端发起的请求的requset信息

     ,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

     //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,

     此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,

     用来判定用户是否有此权限。如果不在权限表中则放行。

     @Override

     public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes)

2、正式实战了

1 使用idea的Srping Initializr 创建一个项目 我的版本如下Pom.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

<? xml version = "1.0" encoding = "UTF-8" ?>

< project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://HdhCmsTestw3.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测试数据piler.target >1.8</ maven测试数据piler.target >

         < maven测试数据piler.source >1.8</ maven测试数据piler.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相关标签,可选可不选-->

         < 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测试数据/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,创建一个springSecurity配置类,你也可以使用配置文件的方法。我这里使用了boot的配置类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

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   //这里启动security

public class SpringSecurityConfig extends WebSecurityConfigurerAdapter{

 

     @Autowired   //下面会编写这个类

     private MyFilterSecurityInterceptor myFilterSecurityInterceptor;

 

     @Autowired    //下面会编写这个类

     private DefaultAccessDeniedHandler defaultAccessDeniedHandler;

 

     @Bean   

     UserDetailsService customUserService(){ //注册UserDetailsService 的bean

         return new CustomUserService();

     }

     @Override

     protected void configure(AuthenticationManagerBuilder auth) throws Exception {

         auth.userDetailsService(customUserService()); //user Details Service验证

     }

 

     @Override

     protected void configure(HttpSecurity http) throws Exception {

         http.exceptionHandling()

                 .accessDeniedHandler(defaultAccessDeniedHandler);

         http.authorizeRequests()

                 .antMatchers( "/css/**" ).permitAll()

                 .anyRequest().authenticated() //任何请求,登录后可以访问

                 .and()

                 .formLogin().loginPage( "/login" ).failureUrl( "/login?error" ).permitAll() //登录页面用户任意访问

                 .and()

                 .logout().permitAll(); //注销行为任意访问

         http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor. class );

     }

 

}

3、自定义SecurityMetadataSource拦截器

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

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;

 

     /**

      * 自定义方法。最好在项目启动时,去数据库查询一次就好。

      * 数据库查询一次 权限表出现的所有要拦截的url

      */

     public void loadResourceDefine(){

         map = new HashMap<>();

         Collection<ConfigAttribute> array;

         ConfigAttribute cfg;

         //去数据库查询 使用dao层。 你使用自己的即可

         List<Permission> permissions = permissionDao.findAll();

         for (Permission permission : permissions) {

             array = new ArrayList<>();

             //下面你可以添加你想要比较的信息过去。 注意的是,需要在用户登录时存储的权限信息一致

             cfg = new SecurityConfig(permission.getName());

             //此处添加了资源菜单的名字,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。

           

             array.add(cfg);

             //用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,

             map.put(permission.getUrl(), array);

         }

 

     }

 

     //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。

     @Override

     public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {

         FilterInvocation filterInvocation = (FilterInvocation) object;

         String fullRequestUrl = filterInvocation.getFullRequestUrl();

         //若是静态资源 不做拦截  下面写了单独判断静态资源方法

         if (isMatcherAllowedRequest(filterInvocation)) {

             System.out.println( "我没有被拦截" +fullRequestUrl);

            return null ;

         }

         //map 为null 就去数据库查

         if (map == null )  {

          loadResourceDefine();

         }

         //测试 先每次都查

         //object 中包含用户请求的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 ;

     }

 

 

     /**

      * 判断当前请求是否在允许请求的范围内

      * @param fi 当前请求

      * @return 是否在范围中

      */

     private boolean isMatcherAllowedRequest(FilterInvocation fi){

         return allowedRequest().stream().map(AntPathRequestMatcher:: new )

                 .filter(requestMatcher -> requestMatcher.matches(fi.getHttpRequest()))

                 .toArray().length > 0 ;

     }

 

     /**

      * @return 定义允许请求的列表

      */

     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 。登录的时候根据用户名去数据库查询用户拥有的权限信息

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

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 ) {

             //根据用户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:此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。

                     grantedAuthorities.add(grantedAuthority);

                 }

             }

             return new User(user.getUsername(), user.getPassword(), grantedAuthorities);

         } else {

             throw new UsernameNotFoundException( "admin: " + username + " do not exist!" );

         }

     }

 

}

自定义AccessDecisionManager

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

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 方法是判定是否拥有权限的决策方法,

     //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合.

     //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();

     //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。

     @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 中循环添加到 GrantedAuthority 对象中的权限信息集合

                 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 ;

     }

}

自定义拦截器

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

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里面有一个被拦截的url

//里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限

//再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够

         InterceptorStatusToken token = super .beforeInvocation(fi);

         try {

//执行下一个拦截器

             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;

     }

}

运行项目就实现了。去试试吧。
记得将自定义拦截器放进security的过滤器链中。

到此这篇关于SpringSecurity实现动态url拦截(基于rbac模型)的文章就介绍到这了,更多相关SpringSecurity 动态url拦截内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.51cto测试数据/u_13427436/3503805

查看更多关于SpringSecurity实现动态url拦截(基于rbac模型)的详细内容...

  阅读:31次