1.前言
本文講述使用javaconfig的方式整合SpringMVC+Mybatis+SpringSecurity實現基於數據庫的權限系統,包括對按鈕的權限控制。
使用技術: springMVC、springsecurity4、mybatis、ehcache、前端使用dataTables表格、ztree。
2.表結構介紹
標準的五張表結構。其中t_resources包含了後臺系統的菜單
五張表分別爲用戶表,角色表,資源表,用戶角色表,角色資源表。
給用戶分配角色,給角色配置權限。形成動態的權限控制。一個用戶可以擁有多個角色,一個角色也可以擁有多個資源(即權限)。
3.mavne配置
<properties>
<spring.version>4.3.5.RELEASE</spring.version>
<springsecurity.version>4.2.1.RELEASE</springsecurity.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${springsecurity.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.18</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.18</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
- 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
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 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
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
4.搭建SpringMVC
首先配置DispatcherServlet:
package com.study.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebProjectConfigInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 加載驅動應用後端的中間層和數據層組件
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
/** 指定配置類
* 加載包含web組件的bean,如控制機器、視圖解析器以及映射處理器
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
- 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
- 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
WebConfig:
主要加載包含WEB的組件bean。如控制器、視圖解析器以及處理映射處理器
package com.study.config
import java.util.List
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
import org.springframework.web.servlet.ViewResolver
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
import org.springframework.web.servlet.view.InternalResourceViewResolver
import org.springframework.web.servlet.view.JstlView
import com.alibaba.fastjson.serializer.SerializerFeature
import com.alibaba.fastjson.support.config.FastJsonConfig
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter
@Configuration
@EnableWebMvc
@ComponentScan("com.study.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver()
resolver.setPrefix("/jsp/")
resolver.setSuffix(".jsp")
resolver.setExposeContextBeansAsAttributes(true)
resolver.setViewClass(JstlView.class)
return resolver
}
//配置靜態資源的處理 使DispatcherServlet對靜態資源的請求轉發到Servlet容器默認的Servlet上,而不是使用DispatcherServlet本身來處理此類請求
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable()
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters)
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter()
FastJsonConfig fastJsonConfig = new FastJsonConfig()
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat
)
fastConverter.setFastJsonConfig(fastJsonConfig)
converters.add(fastConverter)
}
}
- 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
- 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
RootConfig:
主要加載應用中其他bean。這些bean通常是驅動應用後端的中間層和數據層組件。
package com.study.config
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.ComponentScan.Filter
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.FilterType
import org.springframework.web.servlet.config.annotation.EnableWebMvc
@Configuration
@ComponentScan(basePackages={"com.study"},excludeFilters={@Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class)})
public class RootConfig {
}
5.配置Spring Security
配置Delegating-FilterProxy
package com.study.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* 配置Delegating-FilterProxy
* 繼承AbstractSecurityWebApplicationInitializer會自動註冊DelegatingFilterProxy
* 等價於xml配置
* <filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
*
*
*/
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer{
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Spring Security 安全配置
package com.study.config
import javax.annotation.Resource
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.encoding.Md5PasswordEncoder
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.util.matcher.AntPathRequestMatcher
import com.study.security.MyFilterSecurityInterceptor
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @Resource
// private DataSource dataSource
@Resource(name="myUserDetailService")
private UserDetailsService myUserDetailService
@Resource
private MyFilterSecurityInterceptor myFilterSecurityInterceptor
// @Override
// public AuthenticationManager authenticationManagerBean() throws Exception {
// return super.authenticationManagerBean()
// }
@Override
public void configure(WebSecurity web)throws Exception {
// 設置不攔截規則
web.ignoring().antMatchers("/css/**","/js/**","/img/**","/font-awesome/**")
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class)//在正確的位置添加我們自定義的過濾器
.authorizeRequests()
.anyRequest().authenticated()
// .and().formLogin().and()
// .httpBasic()
// 自定義登錄頁面
.and()
.formLogin().loginPage("/jsp/login.jsp")
.failureUrl("/jsp/login.jsp?error=1")
.loginProcessingUrl("/spring_security_check")
.usernameParameter("username")
.passwordParameter("password").permitAll().defaultSuccessUrl("/index.do")
//如果開啓了CSRF 退出則需要使用POST訪問,可以使用以下方式解決,但並不推薦
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
// 登陸成功後跳轉的地址,以及刪除的cookie名稱
.logoutSuccessUrl("/jsp/login.jsp?error=logout")
.invalidateHttpSession(true)
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
//啓用內存用戶存儲
//
//給予數據庫表認證
//配置自定義的用戶服務
auth.userDetailsService(myUserDetailService).passwordEncoder(new Md5PasswordEncoder())
}
}
- 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
- 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
解釋:以上代碼實現了spring Security 安全配置。其中@EnableWebSecurity註解將會啓用WEB安全功能。
configure(WebSecurity web) 通過重載,配置Spring Security 的Filter鏈。
configure(HttpSecurity http)通過重載,配置如何通過攔截器保護請求。以上代碼配置了自定義的過濾器 、自定義登錄頁面、和退出功能。其中自定義的過濾器相當於配置了對資源的授權。
configure(AuthenticationManagerBuilder auth)通過重載,配置user_detail服務,其中可以使用基於內存用戶存儲、基於數據庫表進行認證、基於LDAP進行認證,以及自定義的用戶服務。以上代碼沒註釋的就是本文基於數據庫的自定義權限認證。
自定義認證:
就是在登錄時的認證操作交給Spring Security.在此處需要提供給當前登錄用戶所擁有的權限。即根據用戶名查詢上面t_resources表中的resKey 拼湊成”ROLE_XXX“ 這樣形式的字符串所組成的list ,交給spirngSecurity。
package com.study.security
import java.util.HashSet
import java.util.List
import java.util.Set
import javax.annotation.Resource
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 org.springframework.stereotype.Component
import com.study.model.Resources
import com.study.model.User
import com.study.service.ResourcesService
import com.study.service.UserService
@Component("myUserDetailService")
public class MyUserDetailServiceImpl implements UserDetailsService{
@Resource
private UserService userService
@Resource
private ResourcesService resourcesService
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userService.findUserByName(username)
if(user ==null)
throw new UsernameNotFoundException(username+" not exist!")
Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>()
Resources resources = new Resources()
resources.setUsername(username)
List<Resources> list = resourcesService.loadMenu(resources)
for (Resources r : list) {
authSet.add(new SimpleGrantedAuthority("ROLE_" +r.getResKey()))
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
user.getEnable()==1?true:false,
true,
true,
true,
authSet)
}
}
- 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
- 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
自定義過濾器
使訪問程序時,檢查當前用戶是否擁有當前url的權限
package com.study.security;
import java.io.IOException;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
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.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private MySecurityMetadataSource securityMetadataSource;
@Autowired
private MyAccessDecisionManager accessDecisionManager;
@Resource
private AuthenticationConfiguration authenticationConfiguration;
@PostConstruct
public void init() throws Exception{
super.setAccessDecisionManager(accessDecisionManager);
super.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
private void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
@Override
public Class<? extends Object> getSecureObjectClass() {
return FilterInvocation.class;
}
}
- 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
- 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
以上代碼涉及到MySecurityMetadataSource和MyAccessDecisionManager
MyAccessDecisionManager
package com.study.security;
import java.util.Collection;
import java.util.Iterator;
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.Component;
/**
* <!-- 用戶是否擁有所請求資源的權限 -->
*
*/
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null) {
return;
}
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while(iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
String needPermission = configAttribute.getAttribute();
System.out.println("needPermission is " + needPermission);
for(GrantedAuthority ga : authentication.getAuthorities()) {
if(needPermission.equals(ga.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException(" 沒有權限訪問或未重新登錄! ");
}
public boolean supports(ConfigAttribute attribute) {
return true;
}
public boolean supports(Class<?> clazz) {
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
- 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
需要加載所有資源與權限的關係,即查詢t_resources表中所有resKey與resUrl的對應關係。
package com.study.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
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.stereotype.Component;
import com.study.dao.ResourcesDao;
import com.study.model.Resources;
/**
* 加載資源與權限的對應關係
*/
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Resource
private ResourcesDao resourcesDao;
private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return true;
}
/**
* @PostConstruct是Java EE 5引入的註解,
* Spring允許開發者在受管Bean中使用它。當DI容器實例化當前受管Bean時,
* @PostConstruct註解的方法會被自動觸發,從而完成一些初始化工作,
*
* //加載所有資源與權限的關係
*/
@PostConstruct
private void loadResourceDefine() {
if (resourceMap == null) {
resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
List<Resources> list = resourcesDao.queryAll(new Resources());
for (Resources resources : list) {
Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
ConfigAttribute configAttribute = new SecurityConfig("ROLE_" + resources.getResKey());
configAttributes.add(configAttribute);
resourceMap.put(resources.getResUrl(), configAttributes);
}
}
}
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
if(resourceMap == null) {
loadResourceDefine();
}
if(requestUrl.indexOf("?")>-1){
requestUrl=requestUrl.substring(0,requestUrl.indexOf("?"));
}
Collection<ConfigAttribute> configAttributes = resourceMap.get(requestUrl);
return configAttributes;
}
}
- 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
- 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
6.配置對按鈕的前臺顯示控制
因爲基於了數據庫的權限控制,無法通過使用Spring Security的標籤來控制對按鈕的條件性渲染。不過其實可以通過,來控制顯示。但是按鈕所對應的Role_XXX就被限定死,而無法修改了。但是在開發階段,只有按鈕所對應操作的url是可以確定的。於是採用自定義標籤,使當前的按鈕根據按鈕的url來確定當前用戶是否擁有該url的權限,進而控制按鈕的顯示。自定義標籤如下所示:
AuthorizeTag
package com.study.tag;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.tagext.BodyTagSupport;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.study.model.Resources;
import com.study.service.ResourcesService;
import com.study.util.SpringWiredBean;
/**
* 自定義標籤 用於前臺判斷按鈕權限
* @author A
*
*/
public class AuthorizeTag extends BodyTagSupport {
private static final long serialVersionUID = 1L;
private String buttonUrl;
public String getButtonUrl() {
return buttonUrl;
}
public void setButtonUrl(String buttonUrl) {
this.buttonUrl = buttonUrl;
}
@SuppressWarnings("static-access")
@Override
public int doStartTag() {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
SecurityContextImpl securityContextImpl = (SecurityContextImpl) request
.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
String name = securityContextImpl.getAuthentication().getName();
ResourcesService resourcesService= (ResourcesService) SpringWiredBean.getInstance().getBeanById("resourcesService");
List<Resources> queryAll = resourcesService.queryAll();
boolean flag = true;
for (Resources resources : queryAll) {
if(resources.getResUrl().equals(buttonUrl))
flag = false;
}
if(flag)
return EVAL_BODY_INCLUDE;
else{
Resources resources = new Resources();
resources.setUsername(name);
resources.setResUrl(buttonUrl);
List<Resources> resourcesList = resourcesService.loadMenu(resources);
if(resourcesList.size()>0) return EVAL_BODY_INCLUDE;
}
return this.SKIP_BODY;
}
}
- 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
- 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
以上帶有用到SpringWiredBean
SpringWiredBean
package com.study.util;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
public class SpringWiredBean extends SpringBeanAutowiringSupport {
/**
* 自動裝配註解會讓Spring通過類型匹配爲beanFactory注入示例
*/
@Autowired
private BeanFactory beanFactory;
private SpringWiredBean() {
}
private static SpringWiredBean instance;
static {
instance = new SpringWiredBean();
}
/**
* 實例方法
* 使用的時候先通過getInstance方法獲取實例
*
* @param beanId
* @return
*/
public Object getBeanById(String beanId) {
return beanFactory.getBean(beanId);
}
public static SpringWiredBean getInstance() {
return instance;
}
}
- 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
- 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
在WEB-INF下加入authorize.tld
authorize.tld
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<description>
<![CDATA[security Tags]]>
</description>
<tlib-version>1.0</tlib-version>
<short-name>security</short-name>
<uri>http://www.springsecurity.org/jsp</uri>
<tag>
<description>
<![CDATA[authorize Tag]]>
</description>
<name>authorize</name>
<tag-class>
com.study.tag.AuthorizeTag
</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>buttonUrl</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
- 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
- 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
則在前端使用時,引入上面定義的標籤:
<%@ taglib uri="http://www.springsecurity.org/jsp" prefix="security"%>
然後頁面上控制:
<security:authorize buttonUrl="/role/addRole.do">
<button type="button" id="btn_search" onclick="$('#addRole').modal();" class="btn btn-info" style="float: right; margin-right: 1;">新增</button>
</security:authorize>
7.配置Mybatis
package com.study.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.github.pagehelper.PageHelper;
@Configuration
@PropertySource({"classpath:jdbc.properties"})
@EnableTransactionManagement
@MapperScan(basePackages = {"com.study.dao"})
public class DataSourceConfig {
private static final Logger logger = Logger.getLogger(DataSourceConfig.class);
@Value("${jdbc.driver}")
private String driverClass;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String userName;
@Value("${jdbc.password}")
private String passWord;
@Value("${maxActive}")
private int maxActive;
@Value("${maxIdle}")
private int maxIdle;
@Value("${maxWait}")
private long maxWait;
/**
* 必須加上static
*/
public static PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {
logger.info("PropertySourcesPlaceholderConfigurer");
return new PropertySourcesPlaceholderConfigurer();
}
@Bean(destroyMethod="close")
public BasicDataSource dataSource() {
logger.info("DataSource");
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(passWord);
dataSource.setMaxActive(maxActive);
dataSource.setMaxIdle(maxIdle);
dataSource.setMaxWait(maxWait);
return dataSource;
}
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactory.setMapperLocations(resolver.getResources("classpath:mapping/*Mapping.xml"));
sessionFactory.setPlugins(new Interceptor[]{pageHelper()});
return sessionFactory.getObject();
}
/**
* mybatis 分頁插件配置
* @return
*/
@Bean
public PageHelper pageHelper() {
logger.info("MyBatisConfiguration.pageHelper()");
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
pageHelper.setProperties(p);
return pageHelper;
}
}
- 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
- 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
以上代碼,配置了dataSource、sqlSessionFactory、txManager。還配置了mybatis的分頁插件pageHelper。
需要注意的是:上面代碼註釋了的部分,配置的MapperScannerConfigurer。當寫上這段代碼後,就會使因爲@PropertySource注入的jdbc配置獲取的值爲null,導致datasource無法配置成功,後來找了一些資料,改成@MapperScan(basePackages = {“com.study.dao”})就可以了。但還在沒找到原因,希望哪個大牛看到後可以給我解答,還有因爲這個只是一個練習的項目,可能存在很多不妥或者欠缺的地方,各位對這個項目有建議的地方,希望留言或者發送到郵箱
[email protected],在此先謝謝大家。
再附上CachingConfig
CachingConfig
package com.study.config
import org.apache.log4j.Logger
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.EnableCaching
import org.springframework.cache.ehcache.EhCacheCacheManager
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
@Configuration
@EnableCaching//<!-- 啓用緩存註解 -->
public class CachingConfig {
private static final Logger logger = Logger.getLogger(CachingConfig.class)
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean()
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource(
"ehcache.xml"))
return ehCacheManagerFactoryBean
}
@Bean
public CacheManager cacheManager() {
logger.info("EhCacheCacheManager")
EhCacheCacheManager cacheManager = new EhCacheCacheManager()
cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject())
return cacheManager
}
}
- 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
- 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
8.效果圖
從圖中可以看到,當勾選相應的權限後,界面上的按鈕也跟着消失了。
套了模板,但是界面還是很醜,對前臺操作不太擅長,就先這樣了,還有對所有的修改操作都沒有做,就做了增加和刪除(好吧,有點懶的)。
第一次使用時導入mytest.sql
admin 的密碼爲admin ,因爲密碼的加密是後面加入的,所以現在庫裏只有admin可以登錄。要想使用其他用戶登錄,請使用admin用戶重新創建用戶,
並分配相應的權限。
代碼下載:
github 地址 :https://github.com/lovelyCoder/springsecuritydemo.git
csdn 下載http://download.csdn.net/detail/poorcoder_/9818490
轉載請標明出處:http://blog.csdn.net/poorcoder_/article/details/70231779