一、SpringSecurity-简介

  • 概念
  • Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于spring的应用程序的实际标准。
  • Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它很容易扩展以满足定制需求
  • 4种使用方式
  • ①全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中
  • ②用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置
  • ③细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器, 并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。
  • ④修改springsecurity的源代码,主要是修改InvocationSecurityMetadataSourceService和UserDetailsService两个类。
  • InvocationSecurityMetadataSourceService
  • 将配置文件或数据库中存储的资源(url)提取出来加工成为url和权限列表的Map供Security使用
  • UserDetailsService
  • 提取用户名和权限组成一个完整的(UserDetails)User对象,该对象可以提供用户的详细信息供AuthentationManager进行认证与授权使用

我采用的数据库方式储存权限

Spring安全权限控制 spring实现权限控制_spring

Spring安全权限控制 spring实现权限控制_List_02

Spring安全权限控制 spring实现权限控制_List_03

二、SpringSecurity-HelloWorld

  • 1、引入SpringSecurity的依赖,pom.xml
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-web</artifactId>
     <version>4.2.10.RELEASE</version>
</dependency>
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-config</artifactId>
     <version>4.2.10.RELEASE</version>
</dependency>
<!-- 标签库 -->
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-taglibs</artifactId>
     <version>4.2.10.RELEASE</version>
</dependency>
  • 2、 编写SpringSecurity的配置
  • web.xml配置Security的Filter拦截所有请求
<!-- 权限过滤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>
  • 3、控制登录
  • 编写SpringSecurity的配置类
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;

@Configuration
@EnableWebSecurity
public class AtcrowdfundingSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserDetailsService userDetailsService;

	// 认证和授权
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//new BCryptPasswordEncoder()是对密码的加密算法
		auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		// 设置哪些资源可以被访问
		http.authorizeRequests()
		//这些资源可以直接访问
		.antMatchers("/static/**", "/welcome.jsp", "/login").permitAll()
		//其他的访问需要认证
		.anyRequest().authenticated();

		//设置没有登录不能访问的资源跳转到我们自己的登录页(他存在一个默认的很丑的登录页)
		http.formLogin()
		.loginPage("/login")//请求到我们自己写的登录界面
		.usernameParameter("loginacct")//这里填入我们登陆界面的用户名name属性
		.passwordParameter("userpswd")//密码
		.loginProcessingUrl("/doLogin")//登录信息验证请求
		.defaultSuccessUrl("/main");//登录成功后跳转的界面
		
		//禁用跨站请求伪造,让测试更简单,实际情况通常不会禁用
		http.csrf().disable();

		// 对无权访问的处理
		http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
			@Override
			public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
				String header = request.getHeader("X-Requested-With");
				// 如果是异步请求
				if ("XMLHttpRequest".equals(header)) {
					response.getWriter().print("403");
				} else {
					request.getRequestDispatcher("/WEB-INF/jsp/error/error403.jsp").forward(request, response);
					;
				}
			}
		});

		// 退出登录后跳转的请求
		http.logout().logoutSuccessUrl("/index");
		http.rememberMe();
	}		
}
  • 验证登录信息是否正确,如果正确,查出他拥有的所有权限
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
	
@Component
public class MyUserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private TAdminMapper adminMapper;
	@Autowired
	private TRoleMapper roleMapper;
	@Autowired
	private TPermissionMapper permissionMapper;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 通过用户名认证
		TAdminExample example = new TAdminExample();
		example.createCriteria().andUsernameEqualTo(username);
		List<TAdmin> admins = adminMapper.selectByExample(example);
		TAdmin admin = null;
		if (admins != null && admins.size() != 0) {
			admin = admins.get(0);
		}
		// 认证结束
		// 查询用户角色
		int adminId = admin.getId();
		List<TRole> roles = roleMapper.queryRoleByAdminId(adminId);
		// 查询用户拥有的权限
		List<TPermission> permissions = permissionMapper.queryPermissionByAdminId(adminId);
		// 用户拥有的权限(角色+权限)
		Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
		for (TRole role : roles) {
			authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
		}
		for (TPermission permission : permissions) {
			authorities.add(new SimpleGrantedAuthority(permission.getName()));
		}
		return new User(admin.getLoginacct(), admin.getUserpswd(), authorities);
	}

}
  • 4、细粒度权限功能
  • 加入注解到SpringSecurity的配置类,即上面的AtcrowdfundingSecurityConfig类
//开启细粒度权限功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
  • 可以精确到一个按钮的权限
  • ①是组长身份才能看到“管理组员”按钮
<sec:authorize access="hasRole('TL - 组长')">
	<button type="button" class="btn btn-default btn-danger">
	    <span class="glyphicon glyphicon-question-sign">管理组员</span> 
	</button>
</sec:authorize>
  • ②不隐藏按钮,而是在点击后告诉用户没有权限
  • 我们将权限控制加到方法上面
  • 如果没有权限会跳转的我们最初配置的SpringSecurity的配置类里面的拒绝访问
// 用户新增
@PreAuthorize("hasAuthority('user:add')") 
@RequestMapping("/admin/doAdd")
public String addAdmin(TAdmin admin) {
	adminService.saveAdmin(admin);
	return "redirect:/admin/index?pageNum=" + Integer.MAX_VALUE;
}