ITPub博客

首页 > 架构设计 > 软件结构 > JAVA b2b2c电子商务SpringCloud分布式微服务spring-security-基本

JAVA b2b2c电子商务SpringCloud分布式微服务spring-security-基本

软件结构 作者:gung123 时间:2020-02-10 14:15:11 0 删除 编辑

一、简介

Web应用的安全管理,主要包括两个方面的内容:身份认证、用户授权,此处使用spring-cloud-security来说明。

二、依赖管理

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
   <groupId>org.thymeleaf.extras</groupId>
   <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

三、安全策略配置

Spring Security已经大体实现了,我们这里只是需要一些配置与引用。

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
package  com.example.demo.config;
 
import  com.example.demo.utils.security.CustomUserService;
import  com.example.demo.utils.security.LoginSuccessHandler;
import  com.example.demo.utils.security.MyFilterSecurityInterceptor;
import  com.example.demo.utils.security.SecuritySettings;
import  org.apache.commons.lang3.StringUtils;
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.builders.WebSecurity;
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;
import  org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import  org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
 
/**
  * Security安全配置
  *
  * @Author: 我爱大金子
  * @Description: Security安全配置
  * @Date: Create in 15:20 2017/7/5
  */
@Configuration
@EnableWebSecurity
public  class  WebSecurityConfig  extends  WebSecurityConfigurerAdapter {
 
     @Autowired
     private  CustomFilterSecurityInterceptor customFilterSecurityInterceptor;     // 权限管理过滤器
     @Autowired
     private  SecuritySettings securitySettings;      // 自定义安全配置类
 
     /**注册UserDetailsService的bean*/
     @Bean
     public  UserDetailsService customUserService(){
         return  new  CustomUserService();
     }
 
     /**登录认证*/
     @Override
     protected  void  configure(AuthenticationManagerBuilder auth)  throws  Exception {
         auth.userDetailsService(customUserService());  //userDetailsService验证
 
     }
 
     /***设置不拦截规则*/
     @Override
     public  void  configure(WebSecurity web)  throws  Exception {
         web.ignoring().antMatchers( "/js/**" ,  "/css/**" ,  "/images/**" ,  "/druid/**" );
     }
 
     /**安全策略配置*/
     @Override
     protected  void  configure(HttpSecurity http)  throws  Exception {
         // 设置游客可以访问的URI
         if  (StringUtils.isNotBlank(securitySettings.getPermitall())) {
             http.authorizeRequests().antMatchers(securitySettings.getPermitall().split( "," )).permitAll();
         }
 
         http.authorizeRequests()
                 .anyRequest().authenticated()  //任何请求,登录后可以访问
                 // 配置登录URI、登录失败跳转URI与登录成功后默认跳转URI
                 .and().formLogin().loginPage( "/login" ).failureUrl( "/login?error" ).permitAll().defaultSuccessUrl( "/" ,  true ).successHandler(loginSuccessHandler())
                 // 注销行为任意访问
                 .and().logout().permitAll()
                 // 设置拒绝访问的提示URI
                 .and().exceptionHandling().accessDeniedPage( "/login?illegal" )
         ;
 
         http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor. class );
     }
 
     /**登录成功处理器*/
     private  AuthenticationSuccessHandler loginSuccessHandler() {
         return  new  LoginSuccessHandler();
     }
}

说明:

 loginPage:设置一个实验自定义的登录URI

 loginSuccessHandler:设置自定义的登录处理器

 permitAll:是允许访问

 accessDeniedPage:配置拒绝访问的提示URI

 antMatchers:对URI的配置

了解springcloud架构可以加求求:三五三六二四七二五九

假设我要管理员才可以访问admin文件夹下的内容,如:.antMatchers("/admin/**").hasRole("ROLE_ADMIN"),

也可以设置admin文件夹下的文件可以有多个角色来访问,如:.antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")

也可以通过hasIpAddress来指定某一个ip可以访问该资源,写法如下.antMatchers("/admin/**").hasIpAddress("210.210.210.210") 

3.1、自定义安全配置类 

为是更方便的使用springSecurity,我们自定义一个权限的配置类,如配置登录的URI、游客访问的URI等配置项

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
package  com.example.demo.utils.security;
 
import  org.springframework.boot.context.properties.ConfigurationProperties;
import  org.springframework.context.annotation.Configuration;
 
/**
  * 自定义安全配置类
  *
  * @Author: 我爱大金子
  * @Description: 自定义安全配置类
  * @Date: Create in 9:45 2017/7/6
  */
@Configuration
@ConfigurationProperties (prefix =  "securityConfig" )
public  class  SecuritySettings {
     /**允许访问的URL,多个用逗号分隔*/
     private  String permitall;
 
     public  String getPermitall() {
         return  permitall;
     }
 
     public  void  setPermitall(String permitall) {
         this .permitall = permitall;
     }
}

3.2、登录成功处理器

登录成功后,如果需要对用户的行为做一些记录或者执行其它操作,则可以使用登录成功处理器。

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
package  com.example.demo.utils.security;
 
import  com.example.demo.pojo.SysUser;
import  org.springframework.security.core.Authentication;
import  org.springframework.security.core.userdetails.User;
import  org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
 
import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServletRequest;
import  javax.servlet.http.HttpServletResponse;
import  java.io.IOException;
 
/**
  * 登录成功处理器
  *
  * @Author: 我爱大金子
  * @Description: 登录成功处理器
  * @Date: Create in 11:35 2017/7/6
  */
public  class  LoginSuccessHandler  extends  SavedRequestAwareAuthenticationSuccessHandler {
     @Override
     public  void  onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)  throws  ServletException, IOException {
         User userDetails = (User) authentication.getPrincipal();
         System.out.println( "登录用户:username="  + userDetails.getUsername() +  ", uri="  + request.getContextPath());
         super .onAuthenticationSuccess(request, response, authentication);
     }
}

3.3、springMVC 配置(访问 /login 转向 login.html 页面)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package  com.example.demo.config;
 
import  org.springframework.context.annotation.Configuration;
import  org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import  org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
/**
  * springMVC 配置(注册访问 /login 转向 login.html 页面)
  *
  * @Author: 我爱大金子
  * @Description: springMVC 配置(注册访问 /login 转向 login.html 页面)
  * @Date: Create in 16:24 2017/7/5
  */
@Configuration
public  class  WebMvcConfig  extends  WebMvcConfigurerAdapter {
 
     @Override
     public  void  addViewControllers(ViewControllerRegistry registry) {
         registry.addViewController( "/login" ).setViewName( "login" );
     }
}

四、登录认证

在安全策略配置代码中有,主要看自定义的CustomUserService,此类实现了UserDetailsService接口,重写了loadUserByUsername方法

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.demo.utils.security;
 
import  com.example.demo.dao.SysPermissionDao;
import  com.example.demo.dao.SysUserDao;
import  com.example.demo.pojo.SysPermission;
import  org.springframework.security.core.userdetails.User;
import  com.example.demo.pojo.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.UserDetails;
import  org.springframework.security.core.userdetails.UserDetailsService;
import  org.springframework.security.core.userdetails.UsernameNotFoundException;
 
import  java.util.ArrayList;
import  java.util.List;
 
/**
  * 自定义UserDetailsService,将用户权限交给springsecurity进行管控
  *
  * @Author: 我爱大金子
  * @Description: 将用户权限交给Springsecurity进行管控
  * @Date: Create in 16:19 2017/7/5
  */
public  class  CustomUserService  implements  UserDetailsService {
     @Autowired
     private  SysUserDao sysUserDao;
     @Autowired
     private  SysPermissionDao sysPermissionDao;
 
     @Override
     public  UserDetails loadUserByUsername(String username) {
         SysUser user = sysUserDao.findByUserName(username);
         if  (user !=  null ) {
             List<SysPermission> permissions = sysPermissionDao.findByAdminUserId(user.getId());
             List<GrantedAuthority> grantedAuthorities =  new  ArrayList <>();
             for  (SysPermission 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!" );
         }
     }
}

五、权限管理

在Security安全配置类中使用了权限管理过滤器CustomFilterSecurityInterceptor

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
package  com.example.demo.utils.security;
 
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  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.stereotype.Service;
 
import  java.io.IOException;
 
/**
  * 权限管理过滤器
  *
  * @Author: 我爱大金子
  * @Description: 权限管理过滤器
  * @Date: Create in 17:16 2017/7/5
  */
@Service
public  class  CustomFilterSecurityInterceptor  extends  AbstractSecurityInterceptor  implements  Filter {
     @Autowired
     private  CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;   // 权限配置资源管理器
 
     /**权限管理决断器*/
     @Autowired
     public  void  setMyAccessDecisionManager(CustomAccessDecisionManager customAccessDecisionManager) {
         super .setAccessDecisionManager(customAccessDecisionManager);
     }
  
     @Override
     public  void  init(FilterConfig filterConfig)  throws  ServletException {
 
     }
 
     @Override
     public  void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  throws  IOException, ServletException {
 
         FilterInvocation fi =  new  FilterInvocation(request, response, chain);
         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 .customFilterInvocationSecurityMetadataSource;
     }
}

说明:

 customFilterSecurityInterceptor:权限管理过滤器

 customAccessDecisionManager:权限管理决断器

 customFilterInvocationSecurityMetadataSource:权限配置资源管理器


其中过滤器在系统启动时开始工作,并同时导入权限配置资源管理器和权限管理决断器,对用户访问的资源进行管理。权限管理决断器对用户访问的资源与用户拥有的角色权限进行对比,以此来判断用户是否对某个资源具有访问权限。

5.1、权限管理过滤器

继承与AbstractSecurityInterceptor,实时监控用户的行为,防止用户访问未被授权的资源。

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
package  com.example.demo.utils.security;
 
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  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
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.stereotype.Service;
 
import  java.io.IOException;
 
/**
  * 权限管理过滤器
  *
  * @Author: 我爱大金子
  * @Description: 权限管理过滤器
  * @Date: Create in 17:16 2017/7/5
  */
@Service
public  class  CustomFilterSecurityInterceptor  extends  AbstractSecurityInterceptor  implements  Filter {
     Logger log = LoggerFactory.getLogger(CustomFilterSecurityInterceptor. class );
     @Autowired
     private  CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;   // 权限配置资源管理器
 
     /**权限管理决断器*/
     @Autowired
     public  void  setMyAccessDecisionManager(CustomAccessDecisionManager customAccessDecisionManager) {
         super .setAccessDecisionManager(customAccessDecisionManager);
     }
  
     @Override
     public  void  init(FilterConfig filterConfig)  throws  ServletException {
 
     }
 
     @Override
     public  void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  throws  IOException, ServletException {
 
         FilterInvocation fi =  new  FilterInvocation(request, response, chain);
         log.info( "【权限管理过滤器】请求URL:"  + fi.getRequestUrl());
         invoke(fi);
     }
  
     public  void  invoke(FilterInvocation fi)  throws  IOException, ServletException {
         //fi里面有一个被拦截的url
         //里面调用CustomFilterInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
         //再调用CustomAccessDecisionManager的decide方法来校验用户的权限是否足够
         InterceptorStatusToken token =  super .beforeInvocation(fi);
         try  {
             //执行下一个拦截器
             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
         }  catch (Exception e) {
             log.error( "【权限管理过滤器】【异常】"  + e.getMessage(), e);
         }  finally  {
             super .afterInvocation(token,  null );
         }
     }
 
     @Override
     public  void  destroy() {
 
     }
 
     @Override
     public  Class<?> getSecureObjectClass() {
         return  FilterInvocation. class ;
     }
 
     @Override
     public  SecurityMetadataSource obtainSecurityMetadataSource() {
         return  this .customFilterInvocationSecurityMetadataSource;
     }
}

5.2、权限管理决断器

权限管理的关键部分就是决断器,它实现了AccessDecisionManager,重写了decide方法,使用自定义的决断器,在用户访问受保护的资源时,决断器判断用户拥有的角色中是否对改资源具有访问权限,如果没有,则拒绝访问

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
package  com.example.demo.utils.security;
 
import  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
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;
 
/**
  * 权限管理决断器
  *
  * @Author: 我爱大金子
  * @Description: 权限管理决断器
  * @Date: Create in 17:15 2017/7/5
  */
@Service
public  class  CustomAccessDecisionManager  implements  AccessDecisionManager {
     Logger log = LoggerFactory.getLogger(CustomAccessDecisionManager. class );
     
     // 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 object, 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 ;
                 }
             }
             log.info( "【权限管理决断器】需要role:"  + needRole);
         }
         throw  new  AccessDeniedException( "Access is denied" );
     }
 
     @Override
     public  boolean  supports(ConfigAttribute attribute) {
         return  true ;
     }
 
     @Override
     public  boolean  supports(Class<?> clazz) {
         return  true ;
     }
}


5.3、权限配置资源管理器

权限配置资源管理器实现了FilterInvocationSecurityMetadataSource, 在启动时就去加载了所有的权限列表,权限配置资源管理器为决断器实时提供支持,判断用户访问的资源是否在受保护的范围之内。

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
package  com.example.demo.utils.security;
 
import  com.example.demo.dao.SysPermissionDao;
import  com.example.demo.pojo.SysPermission;
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.*;
 
/**
  * 权限配置资源管理器
  *
  * @Author: 我爱大金子
  * @Description: 权限配置资源管理器
  * @Date: Create in 17:17 2017/7/5
  */
@Service
public  class  CustomFilterInvocationSecurityMetadataSource  implements  FilterInvocationSecurityMetadataSource {
     @Autowired
     private  SysPermissionDao sysPermissionDao;
 
     private  HashMap<String, Collection<ConfigAttribute>> map = null ;
 
     /**
      * 加载权限表中所有权限
      */
     public  void  loadResourceDefine(){
         map =  new  HashMap<>();
         Collection<ConfigAttribute> array;
         ConfigAttribute cfg;
         List<SysPermission> permissions = sysPermissionDao.findAll();
         for (SysPermission 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 {
         if (map == null ) loadResourceDefine();
         //object 中包含用户请求的request 信息
         HttpServletRequest request = ((FilterInvocation) object).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 ;
     }
 
     @Override
     public  Collection<ConfigAttribute> getAllConfigAttributes() {
         return  null ;
     }
 
     @Override
     public  boolean  supports(Class<?> clazz) {
         return  true ;
     }
}

六、根据权限设置连接

对于权限管理,我们可能希望,在一个用户访问的界面中,不是等到用户点击了超链接之后,才来判断用户有没有这个权限,而是按照用户拥有的权限来显示超链接。这样的设计对于用户体验来说,会更友好。

6.1、方法1:使用sec标签(thymeleaf)

在html标签中引入的Spring Security的标签:

1
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"

sec:authentication="name":取当前登录用户的用户名

1
< title  sec:authentication = "name" ></ title >

sec:authorize="hasRole('ROLE_ADMIN'):表示当前用户是否拥有角色ROLE_ADMIN

1
< li  sec:authorize = "hasRole('ROLE_ADMIN')" >< a  th:href = "@{/admin}" > admin </ a ></ li >

sec:authorize="hasAuthority('admin')":表示当前用户是否拥有权限admin

1
< li  sec:authorize = "hasAuthority('admin')" >< a  th:href = "@{/admin}" > admin </ a ></ li >

6.2、方法二:代码

在控制层用代码获取是否有权限,然后将标识放入内容中,页面获取处理

七、其它代码

7.1、controller

IndexController.java

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
package  com.example.demo.controller;
 
import  com.example.demo.domain.Msg;
import  org.springframework.stereotype.Controller;
import  org.springframework.ui.Model;
import  org.springframework.web.bind.annotation.RequestMapping;
import  org.springframework.web.bind.annotation.ResponseBody;
 
/**
  * 描述
  *
  * @Author: 我爱大金子
  * @Description: 描述
  * @Date: Create in 15:25 2017/7/5
  */
@Controller
public  class  IndexController {
     /**系统首页*/
     @RequestMapping ( "/" )
     public  String index(Model model){
         Msg msg =   new  Msg( "测试标题" , "测试内容" , "欢迎来到HOME页面,您拥有index权限" );
         model.addAttribute( "msg" , msg);
         return  "home" ;
     }
 
     /**系统首页2*/
     @RequestMapping ( "/index2" )
     public  String index2(Model model){
         Msg msg =   new  Msg( "测试标题2" , "测试内容2" , "欢迎来到HOME页面,您拥有home权限" );
         model.addAttribute( "msg" , msg);
         return  "home" ;
     }
 
     @RequestMapping ( "/admin" )
     @ResponseBody
     public  String hello(){
         return  "hello admin" ;
     }
 
     @RequestMapping ( "/yk" )
     @ResponseBody
     public  String hello2(){
         return  "hello yk" ;
     }
}

7.2、dao

SysUserDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package  com.example.demo.dao;
 
import  com.example.demo.pojo.SysUser;
import  org.apache.ibatis.annotations.Mapper;
 
/**
  * 系统用户dao
  *
  * @Author: 我爱大金子
  * @Description: 系统用户dao
  * @Date: Create in 16:15 2017/7/5
  */
@Mapper
public  interface  SysUserDao {
     public  SysUser findByUserName(String username);
}

SysPermissionDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package  com.example.demo.dao;
 
import  com.example.demo.pojo.SysPermission;
import  org.apache.ibatis.annotations.Mapper;
 
import  java.util.List;
 
/**
  * 系统权限dao
  *
  * @Author: 我爱大金子
  * @Description: 描述
  * @Date: Create in 17:05 2017/7/5
  */
@Mapper
public  interface  SysPermissionDao {
     List<SysPermission> findAll();
     List<SysPermission> findByAdminUserId(Long userId);
}

7.3、domain

Msg.java

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
package  com.example.demo.domain;
 
/**
  * 描述
  *
  * @Author: 我爱大金子
  * @Description: 描述
  * @Date: Create in 16:14 2017/7/5
  */
public  class  Msg {
     private  String title;
     private  String content;
     private  String etraInfo;
     public  Msg(String title, String content, String etraInfo) {
         super ();
         this .title = title;
         this .content = content;
         this .etraInfo = etraInfo;
     }
     public  String getTitle() {
         return  title;
     }
     public  void  setTitle(String title) {
         this .title = title;
     }
     public  String getContent() {
         return  content;
     }
     public  void  setContent(String content) {
         this .content = content;
     }
     public  String getEtraInfo() {
         return  etraInfo;
     }
     public  void  setEtraInfo(String etraInfo) {
         this .etraInfo = etraInfo;
     }
}

7.4、pojo

SysUser.java

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.demo.pojo;
 
import  java.util.List;
 
/**
  * 系统用户
  *
  * @Author: 我爱大金子
  * @Description: 系统用户
  * @Date: Create in 16:12 2017/7/5
  */
public  class  SysUser {
     private  Long id;
     private  String username;
     private  String password;
 
     private  List<SysRole> roles;
 
     public  Long getId() {
         return  id;
     }
 
     public  void  setId(Long id) {
         this .id = id;
     }
 
     public  String getUsername() {
         return  username;
     }
 
     public  void  setUsername(String username) {
         this .username = username;
     }
 
     public  String getPassword() {
         return  password;
     }
 
     public  void  setPassword(String password) {
         this .password = password;
     }
 
     public  List<SysRole> getRoles() {
         return  roles;
     }
 
     public  void  setRoles(List<SysRole> roles) {
         this .roles = roles;
     }
}

SysRole.java

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
package  com.example.demo.pojo;
 
/**
  * 系统角色
  *
  * @Author: 我爱大金子
  * @Description: 系统角色
  * @Date: Create in 16:13 2017/7/5
  */
public  class  SysRole {
     private  Long id;
     private  String name;
 
     public  Long getId() {
         return  id;
     }
 
     public  void  setId(Long id) {
         this .id = id;
     }
 
     public  String getName() {
         return  name;
     }
 
     public  void  setName(String name) {
         this .name = name;
     }
}

SysPermission.java

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
package  com.example.demo.pojo;
 
/**
  * 系统权限
  *
  * @Author: 我爱大金子
  * @Description: 系统权限
  * @Date: Create in 17:04 2017/7/5
  */
public  class  SysPermission {
     private  Long id;
     //权限名称
     private  String name;
 
     //权限描述
     private  String descritpion;
 
     //授权链接
     private  String url;
 
     //父节点id
     private  int  pid;
 
     public  Long getId() {
         return  id;
     }
 
     public  void  setId(Long id) {
         this .id = id;
     }
 
     public  String getName() {
         return  name;
     }
 
     public  void  setName(String name) {
         this .name = name;
     }
 
     public  String getDescritpion() {
         return  descritpion;
     }
 
     public  void  setDescritpion(String descritpion) {
         this .descritpion = descritpion;
     }
 
     public  String getUrl() {
         return  url;
     }
 
     public  void  setUrl(String url) {
         this .url = url;
     }
 
     public  int  getPid() {
         return  pid;
     }
 
     public  void  setPid( int  pid) {
         this .pid = pid;
     }
}

7.5、mapperXX.xml

SysUserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<? xml  version = "1.0"  encoding = "UTF-8"  ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper  namespace = "com.example.demo.dao.SysUserDao"  >
     < resultMap  id = "userMap"  type = "SysUser" >
         < id  property = "id"  column = "ID" />
         < result  property = "username"  column = "username" />
         < result  property = "password"  column = "PASSWORD" />
         < collection  property = "roles"  ofType = "SysRole" >
             < result  column = "name"  property = "name" />
         </ collection >
     </ resultMap >
     < select  id = "findByUserName"  parameterType = "String"  resultMap = "userMap" >
         select u.*
         ,r.name
         from sys_user u
         LEFT JOIN sys_user_role sru on u.id= sru.sys_user_id
         LEFT JOIN sys_role r on sru.sys_role_id=r.id
         where username= #{username}
     </ select >
</ mapper >

SysPermissionDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<? xml  version = "1.0"  encoding = "UTF-8"  ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper  namespace = "com.example.demo.dao.SysPermissionDao"  >
     < select  id = "findAll"   resultType = "SysPermission" >
         SELECT * from sys_permission ;
     </ select >
 
     < select  id = "findByAdminUserId"  parameterType = "java.lang.Long"  resultType = "SysPermission" >
 
       SELECT
         p.*
       FROM sys_user u
       LEFT JOIN sys_user_role sru ON u.id= sru.sys_user_id
       LEFT JOIN sys_role r ON sru.sys_role_id=r.id
       LEFT JOIN sys_role_permission spr ON spr.sys_role_id=r.id
       LEFT JOIN Sys_permission p ON p.id =spr.sys_permission_id
       WHERE u.id=#{userId}
  </ select >
</ mapper >

7.6、html

login.html

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
<!DOCTYPE html>
< html  xmlns:th = "http://www.thymeleaf.org" >
< head >
     < meta  content = "text/html;charset=UTF-8" />
     < title >登录页面</ title >
     < link  rel = "stylesheet"  th:href = "@{css/bootstrap.min.css}" />
     < style  type = "text/css" >
         body {
             padding-top: 50px;
         }
         .starter-template {
             padding: 40px 15px;
             text-align: center;
         }
     </ style >
</ head >
< body >
 
< nav  class = "navbar navbar-inverse navbar-fixed-top" >
     < div  class = "container" >
         < div  class = "navbar-header" >
             < a  class = "navbar-brand"  href = "#" >Spring Security演示</ a >
         </ div >
         < div  id = "navbar"  class = "collapse navbar-collapse" >
             < ul  class = "nav navbar-nav" >
                 < li >< a  th:href = "@{/}" > 首页 </ a ></ li >
 
             </ ul >
         </ div > <!--/.nav-collapse -->
     </ div >
</ nav >
< div  class = "container" >
 
     < div  class = "starter-template" >
         < p  th:if = "${param.logout}"  class = "bg-warning" >已成功注销</ p > <!-- 1 -->
         < p  th:if = "${param.illegal}"  class = "bg-warning" >无权访问,请切换账号登录</ p > <!-- 1 -->
         < p  th:if = "${param.error}"  class = "bg-danger" >用户名或密码错误</ p >  <!-- 2 -->
         < h2 >使用账号密码登录</ h2 >
         < form  name = "form"  th:action = "@{/login}"  action = "/login"  method = "POST" >  <!-- 3 -->
             < div  class = "form-group" >
                 < label  for = "username" >账号</ label >
                 < input  type = "text"  class = "form-control"  name = "username"  value = ""  placeholder = "账号"  />
             </ div >
             < div  class = "form-group" >
                 < label  for = "password" >密码</ label >
                 < input  type = "password"  class = "form-control"  name = "password"  placeholder = "密码"  />
             </ div >
             < input  type = "submit"  id = "login"  value = "Login"  class = "btn btn-primary"  />
         </ form >
     </ div >
</ div >
</ body >
</ html >

home.html



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
<!DOCTYPE html>
< html  xmlns:th = "http://www.thymeleaf.org"
       xmlns:sec = "http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" >
< head >
     < meta  content = "text/html;charset=UTF-8" />
     < title  sec:authentication = "name" ></ title >
     < link  rel = "stylesheet"  th:href = "@{css/bootstrap.min.css}"  />
     < style  type = "text/css" >
         body {
             padding-top: 50px;
         }
         .starter-template {
             padding: 40px 15px;
             text-align: center;
         }
     </ style >
</ head >
< body >
< nav  class = "navbar navbar-inverse navbar-fixed-top" >
     < div  class = "container" >
         < div  class = "navbar-header" >
             < a  class = "navbar-brand"  href = "#" >Spring Security演示</ a >
         </ div >
         < div  id = "navbar"  class = "collapse navbar-collapse" >
             < ul  class = "nav navbar-nav" >
                 < li >< a  th:href = "@{/}" > 首页 </ a ></ li >
                 < li  sec:authorize = "hasAuthority('admin')" >< a  th:href = "@{/admin}" > admin </ a ></ li >
             </ ul >
         </ div > <!--/.nav-collapse -->
     </ div >
</ nav >
  
< div  class = "container" >
 
     < div  class = "starter-template" >
         < h1  th:text = "${msg.title}" ></ h1 >
 
         < p  class = "bg-primary"  th:text = "${msg.content}" ></ p >
 
         < div >
             < p  class = "bg-info"  th:text = "${msg.etraInfo}" ></ p >
         </ div >
         < form  th:action = "@{/logout}"  method = "post" >
             < input  type = "submit"  class = "btn btn-primary"  value = "注销" />
         </ form >
     </ div >
 
</ div >
  
</ body >
</ html >

源码下载地址
转载于: https://yq.aliyun.com/articles/557429?spm=a2c4e.11153940.0.0.b9037f7eOUXOJh



来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69952307/viewspace-2674986/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-11-01

  • 博文量
    200
  • 访问量
    79622