JAVA b2b2c電子商務SpringCloud分散式微服務spring-security-基本

gung123發表於2020-02-10

一、簡介

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"
         "
< 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"
         "
< 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 = " >
< 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 = "
       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 >


轉載於:



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952307/viewspace-2674986/,如需轉載,請註明出處,否則將追究法律責任。

相關文章