SpringSecurity的快速搭建(ssm项目)

首先我们要知道spring security适应来干嘛,主要功能是什么,

Spring Security 参考手册
这是springsecurity的官方手册,如果需要详细了解可前往此处;

  • spring的核心功能主要有三个:
    – 认证(认证你的角色是什么)
    – 授权(你能干什么)
    – 攻击防护(防止伪造是否)
  • 核心就是一组通过职责链的过滤器链;

第一步当然是先要将相关的依赖包加入,这里我加入到我ssm项目中的父项目中

<dependencies>
		<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>

	</dependencies>

第二步就是编写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>/*

第三步就是编写SpringSecurity的配置类;给个模版吧




@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	UserDetailsService userDetailsService;
	
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		// TODO Auto-generated method stub
		//super.configure(auth);
		auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
	}
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/static/**","/login.jsp").permitAll()
		.anyRequest().authenticated();//剩下都需要认证
		// /login.jsp==POST  用户登陆请求发给Security
		http.formLogin().loginPage("/toLogin")
		.usernameParameter("loginacct").passwordParameter("userpswd")
		.loginProcessingUrl("/login")
		.defaultSuccessUrl("/main").permitAll();
		
		http.csrf().disable();
		http.logout().logoutSuccessUrl("/index");
		
		//异常处理器
		http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {

			@Override
			public void handle(HttpServletRequest request, HttpServletResponse response,
					AccessDeniedException accessDeniedException) throws IOException, ServletException {
				String type=request.getHeader("X-Requested-With");
				if ("XMLHttpRequest".equals(type)) {

					//ajax 通过流的形式
					//response.getWriter().print("403");//403权限不够
				}else {
					request.getRequestDispatcher("/WEB-INF/jsp/error/error403.jsp").forward(request, response);
				}

			}
		});
		
		
		http.rememberMe();
	}
	
}

  • 可以看到这个配置类是需要继承springsecurity所提供的WebSecurityConfigurerAdapter的适配器,且还得通过注解开启相关功能
  • @EnableWebSecurity // 声明式配置,启用SpringSecurity安全机制
  • @EnableGlobalMethodSecurity(prePostEnabled = true) //开启细粒度全局方法级别权限控制功能 需要实现细粒话控制就得开启这个
  • 注意的事项:
    – 如果想开启csrf防跨站攻击,必须post方式的/logout请求,表单中需要增加csrf token;如果不禁用csrf,默认是开启的状态;页面不设置csrf表单域,那么,提交登录请求会报错 刷新页面其中的值是不会变化,一旦你登入成功才会改变;如果你登入成功,这个值会被删除,因为他是保持在session中,查询回到登陆页和后退页面,这个值就会重新生成

UserDetailsService类 通过自定义DAO认证方式 最终返回的是一个SpringSecurity所提供的User,在我博客中对SpringSecurity表单认证的过滤器有解释这个类;



@Component
public class SecurityUserDetailServiceImpl implements UserDetailsService {

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

	Logger log=LoggerFactory.getLogger(SecurityUserDetailServiceImpl.class);
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		/*
		 *  SELECT	t_role.* FROM t_role JOIN t_admin_role ON t_role.id = t_admin_role.roleid WHERE t_admin_role.adminid=2
		 * 
		 * 
		 * */
		// 1查询用户对象
		TAdminExample example = new TAdminExample();
		example.createCriteria().andLoginacctEqualTo(username);
		List<TAdmin> list = adminMapper.selectByExample(example);
		log.debug("用户信息{}",list);
		if (list != null && list.size() == 1) {
			TAdmin admin = list.get(0);
			Integer adminId = admin.getId();
			
			log.debug("用户信息{}",admin);
			
			// 1查询角色集合
			List<TRole> roleList = roleMapper.listRoleByAdminId(adminId);
			
			log.debug("用户角色{}",roleList);
			// 查询权限集合
			List<TPermission> permissionList = permissionMapper.listPermissionByAdminId(adminId);
			// 构建用户所有权限集合==》(ROLE_角色+权限)
			Set<GrantedAuthority> authorities=new HashSet<GrantedAuthority>();
			for (TRole role : roleList) {
				authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
			}
			for (TPermission permission : permissionList) {
				authorities.add(new SimpleGrantedAuthority("ROLE_"+permission.getName()));
			}
			
			log.debug("用户总权限集合{}",authorities);
			
			
			// 第一个参数分别是 账号 密码 权限集合
			//return new User(admin.getLoginacct(), admin.getUserpswd(), authorities);
			
			return new TSecurityAdmin(admin, authorities);
		}else {
			return null;
		}

		
	}

}

  • 为什么我会把系统原先的User类给注释,因为可以通过继承来封装一个自定义的User类;原因是如果还需要往里面增加其他属性就显得力不从心,因为User的构造方法最多只提供了7个属性的赋值,上篇博客有讲到这些属性在源码中的应用;因为只能存储到账号密码和密码,但是在实际的使用上,我们还需要使用查询的用户的其他的属性;

public class TSecurityAdmin extends User {

	TAdmin admin;
	public TSecurityAdmin(TAdmin admin,Set<GrantedAuthority> authorities) {	
		super(admin.getLoginacct(), admin.getUserpswd(), true, true, true, true, authorities);
		this.admin=admin;
	}	
}

然后就是让SpringMVC来扫描所有的组件,因为细粒化控制注解需加在controller中,原因是Security的配置是在根容器(Spring配置文件)Spring配置文件没有扫SpringMVC的东西,导致SpringMVC中的组件不能被,所以在controller中的权限控制注解不成功

  • 解决方法1:在controller方法里的service中的具体方法实现累添加权限注解,因为除了controller的组件都是spring管理;
  • 方法2:删除spring ioc容器,让springMVC扫描全部组件;这里我们选择这种

所以在web.xml中注释掉了spring容器;让后将spring容器的配置文件加入到springMVC中:

<!-- 核心控制器 -->
	<!-- The front controller of this Spring Web application, responsible for 
		handling all application requests -->
	<servlet>
		<servlet-name>springDispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>
			classpath*:/spring/springmvc.xml
			classpath*:/spring/spring-*.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<!-- 创建Spring IOC容器 -->
	<!-- needed for ContextLoaderListener -->
	<!-- <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath*:/spring/spring-*.xml</param-value>
	</context-param>

	Bootstraps the root web application context before servlet initialization
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener> -->

然后就是给我们的方法标注权限注解了比如在Controller中,控制doAdd这个方法的访问只能是项目经理


	@PreAuthorize("hasRole('项目经理')")
	@RequestMapping("admin/doAdd")
	public String doAdd(TAdmin admin) {
		adminService.saveTAdmin(admin);
		
		return "redirect:/admin/index?pageNum="+Integer.MAX_VALUE;
	

页面中元素也是可以被控制的,通过springsecurity的jsp标签来实现;如下面代码:认证的用户的角色钥匙组长才能看到这个帮助的图标

<sec:authorize access="hasRole('组长')">
<button type="button" class="btn btn-default btn-danger">
    <span class="glyphicon glyphicon-question-sign"></span> 帮助
</button>
</sec:authorize>

你可能感兴趣的:(SpringSecurity,Spring,无聊写博客mmd)