1、Spring Security是什么?
Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。Spring Security的实现与RBAC模型密不可分。
2、RBAC是什么?
RBAC字面上的意思是基于角色的访问控制(Role-Based Access Control)。在RBAC模型中,用户具有角色,角色具有权限,而资源需要相应的权限或者角色才被允许访问。
3、整合Spring Security
i、添加依赖
给pom.xml文件添加Spring Security的相关依赖
<!-- SpringSecurity 对 Web 应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
ii、添加配置
在web.xml中添加SpringSecurity的过滤器
<!-- SpringSecurity 的 Filter -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
iii、添加配置类
在项目中创建Spring Security的配置类CrowdfundingSecurityConfig,该类继承自WebSecurityConfigurerAdapter父类,代码如下:
@Configuration // 表示当前类为配置类
@EnableWebSecurity // 启用Web安全
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启基于方法的安全认证机制,也就是说在controller层启用注解机制的安全确认
public class CrowdfundingSecurityConfig extends WebSecurityConfigurerAdapter {
private final CrowdUserDetailService userDetailService;
// 采用自定义的userDetailService,以便从数据库中获取账户信息
public CrowdfundingSecurityConfig(CrowdUserDetailService userDetailService) {
this.userDetailService = userDetailService;
}
// 加盐加密,将该对象加入IoC容器
@Bean
public BCryptPasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(getPasswordEncoder());
}
// 权限配置
@Override
protected void configure(HttpSecurity http) throws Exception {
// 设置静态文件可以无权限访问,设置管理员页面需要用户管理角色才可访问
// 以及设置登录页,登录处理,登录失败,登录成功页,登出页
http.authorizeRequests()
.antMatchers("/bootstrap/**","/crowd/**","/css/**","/fonts/**","/img/**","/jquery/**"," /layer/**","/script/**","/ztree/**")
.permitAll()
.antMatchers("/admin/get/page.html")
.hasRole("用户管理")
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletRequest.setAttribute(CrowdConstant.ATTR_NAME_EXCEPTION, new Exception(CrowdConstant.MESSAGE_ACCESS_DENIED));
httpServletRequest.getRequestDispatcher("/WEB-INF/system-error.jsp").forward(httpServletRequest,httpServletResponse);
}
})
.and()
.formLogin()
.loginPage("/admin/to/login/page.html")
.permitAll()
.loginProcessingUrl("/security/do/login.html")
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.defaultSuccessUrl("/admin/to/main/page.html")
.permitAll()
.and()
.logout()
.logoutUrl("/security/do/logout.html")
.logoutSuccessUrl("/admin/to/login/page.html")
.and()
.csrf()
.disable();//禁用CSRF功能,实际开发中不要禁用
}
}
该配置类放在了SpringMVC的扫描目录下,因此只有当SpringMVC的IoC容器启动后,才能找到Spring Security的过滤器。
以下为自定的UserDetailsService,为的是从数据库中获取账户信息:
@Component
public class CrowdUserDetailService implements UserDetailsService {
private final AdminService adminService;
private final RoleService roleService;
private final AuthService authService;
public CrowdUserDetailService(AdminService adminService, RoleService roleService, AuthService authService) {
this.adminService = adminService;
this.roleService = roleService;
this.authService = authService;
}
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 获取账户名为s的管理员账户信息
Admin admin = adminService.getAdmin(new Admin(s, null)).get(0);
// 获取分配给该账户的角色列表
List<Role> assignedRoleList = roleService.getAssignedRole(admin.getId());
// 获取分配给该账户的权限
List<String> authNameList = authService.getAssignedAuthNameByAdminId(admin.getId());
// 构造GrantedAuthority的集合
List<GrantedAuthority> authorities = new ArrayList<>();
// 根据Spring Security的定义,角色前面要加“ROLE_”
for (Role role : assignedRoleList) {
String roleName = "ROLE_" + role.getName();
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(roleName);
authorities.add(simpleGrantedAuthority);
}
for (String auth : authNameList) {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(auth);
authorities.add(simpleGrantedAuthority);
}
// 返回账户信息和GrantedAuthority集合
return new SecurityAdmin(admin, authorities);
}
}
iv、配置资源权限
a、配置页面访问权限
在配置类的configure(HttpSecurity http)方法中可以进行页面访问权限配置。如下代码,设置了静态文件可以无权限访问,设置管理员页面需要用户管理角色才可访问。
http.authorizeRequests()
.antMatchers("/bootstrap/**","/crowd/**","/css/**","/fonts/**","/img/**","/jquery/**"," /layer/**","/script/**","/ztree/**")
.permitAll()
.antMatchers("/admin/get/page.html")
.hasRole("用户管理")
b、配置方法访问权限
@PreAuthorize表示在执行该方法前,需要判断当前用户是否具有相应角色或相应权限。如下代码,在执行save方法前,用户需要拥有”user:add”权限。
@PreAuthorize("hasAuthority('user:add')")
@RequestMapping("/do/add.html")
public String save(Admin admin){
······
}
c、配置页面元素访问权限
在网页上可以使用标签 security:authorize 对元素进行权限控制。如下代码,只有当用户具有角色“用户管理”时,才能在页面上看到标签内容。
<security:authorize access="hasRole('用户管理')">
<li style="height:30px;">
<a href="/admin/get/page.html"><i class="glyphicon glyphicon-user"></i> 用户维护</a>
</li>
</security:authorize>
4、注意事项
在开发过程中遇到过一个BUG,程序报了 springSecurityFilterChain 找不到的错,那是因为我的程序中有两个IoC容器,分别是 Spring 和 Spring MVC 的IoC容器,在程序运行的时候,是先加载的Spring IoC,后加载的Spring MVC IoC,而 springSecurityFilterChain 这个Bean是在Spring MVC IoC里面的,所以在程序运行一开始,会找不到 springSecurityFilterChain,然后就报错了,解决方案就是将两个IoC容器合二为一个。