Spring实战17——Spring Security

本文详细介绍了Spring Security,这是一个基于Spring AOP和Servlet规范的过滤器实现的安全框架。它使用Servlet Filter保护Web请求并限制URL级别的访问,同时利用Spring AOP来保护方法调用。通过核心、Web和配置模块,Spring Security提供了全面的安全解决方案。配置中,通过DelegatingFilterProxy注册FilterChainProxy,以Java方式设置Web安全性,并通过configure方法定制Filter链、保护请求及认证管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • Spring Security 是一种基于Spring AOP 和Servlet 规范中的Filter 实现的安全框架
  • Spring Security 充分利用了依赖注入DI 和面向切面技术
  • Spring Securty 使用Servlet 规范中的Filter 保护web 请求并限制url 级别的访问。它还使用了Spring AOP 保护方法的调用——借助于对象代理和使用通知。

  • 引入core,web 和config 模块
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-core</artifactId>
	    <version>4.2.2.RELEASE</version>
	</dependency>
  	
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-config</artifactId>
	    <version>4.2.2.RELEASE</version>
	</dependency>

	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-web</artifactId>
	    <version>4.2.2.RELEASE</version>
	</dependency>

 

  • 过滤web 请求,Spring Security 借助一系列Servlet Filter 来提供各种安全功能。DelegatingFilterProxy 是一个特殊的Servlet Filter,它将工作委托给一个Filter 实现类,这个实现类作为一个<bean>注入到Spring 应用上下文中。 实际上,它会拦截请求,并将请求委托给id 为springSecurityFilterChain 的bean。
  • springSecurityFilterChain  本身是一个特殊的Filter,被称为FilterChainProxy,它可以链接多个其他Filter。当启动web 安全性时,会自动创建Spring Security 依赖的一系列Filter。

 

1、借助WebApplicationInitializer 以java 方式配置DelegatingFilterProxy,AbstractSecurityWebApplicationInitializer 实现了WebApplicationInitializer,因此Spring 会发现它,并在web 容器中注册DelegatingFilterProxy

package com.qhf.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

/**
 * #配置DelegatingFilterProxy 两种方式: 
 * 1.web.xml 中配置filter
 * <filter> 
 * 	<filter-name>springSecurityFilterChain</filter-name>
 * 	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 * </filter>
 * 
 * 2.自定义类来继承AbstractSecurityWebApplicationInitializer
 * AbstractSecurityWebApplicationInitializer 实现了WebApplicationInitializer,
 * ~故spring 会发现它,并且用它在web 容器中注册DelegatingFilterProxy。
 *
 * DelegatingFilterProxy 会拦截发往应用中的请求,并将请求委托给id 为springSecurityFilterChain 的bean
 */
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {

}

 

2、启动web 安全性功能,当然需要重写三个方法

  • @EnableWebMvcSecurity 注解配置了一个参数解析器,处理器方法能通过带有@AuthenticationPrincipal 注解的参数获得认证用户的principal 或username。它还同时配置了一个bean,当使用Spring表单绑定标签库来定义表单时,这个bean 会自动添加一个隐藏的跨站请求伪造(CSRF) token 输入域。
@Configuration
@EnableWebSecurity    //@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

}

 

3、重写的三个方法

  • configure(WebSecurity):配置Filter链
  • configure(HttpSecurity):配置如何通过拦截器保护请求
  • configure(AuthenticationManagerBuilder):配置user-detail 服务
package com.qhf.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.crypto.password.StandardPasswordEncoder;

import com.qhf.dao.UserDao;
import com.qhf.service.UserServiceImpl;

/**
 * #SpringSecurity 必须配置在一个
 * 	实现了WebSecurityConfigurer 或者继承WebSecurityConfigurerAdapter 的bean 中
 *
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	//需要重载3个方法,默认使用的Filter 链指定了所有http 请求都需要进行认证,当我们没有用户时,请求存储用户是不可能的,所以没有人能登陆成功
	
	DataSource dataSource = null;
	@Autowired
	UserDao userDao;
	/**
	 * #配置用户存储
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		
		/*	
		 * 1.配置内存级别的用户存储
		 * roles() 方法是authorities() 方法的简写,roles() 方法会在给定的值前面添加一个“ROLE_”前缀,并将其作为权限授予用户。
		 */
		auth.inMemoryAuthentication()
			.withUser("user").password("123").roles("USER")
			.and()
			.withUser("admin").password("234").roles("USER", "ADMIN");
		
		
		/*	
		 * 2.配置基于jdbc 数据库级别的用户存储
		 * passwordEncoder() 中参数接收实现PasswordEncoder 接口的任意实现类, spring
		 * 	提供了StandardPasswordEncoder,BCryptPasswordEncoder,NoOpPasswordEncoder
		 * 	我们可以自定义类实现PasswordEncoder 接口,并且通过接口定义的matches 方法来比对数据库取出的加密后密码和请求中的密码是否一致
		 * 	认证查询,权限查询,群组权限查询
		 */		
		auth.jdbcAuthentication()
			.dataSource(dataSource)
			.usersByUsernameQuery("select username, password from table where username = ?")
			.authoritiesByUsernameQuery("select usrname, 'ROLE_USER' from table where username = ?")
			.passwordEncoder(new StandardPasswordEncoder("abc123"));
		
		
		/*
		 * 3.基于LDAP 进行认证,暂时没学习到
		 * auth.ldapAuthentication() .userSearchBase("ou=people")
		 * .userSearchFilter("(uid={0})") .groupSearchBase("ou=groups")
		 * .groupSearchFilter("member={0}")
		 * .contextSource().root("dc=habuma,dc=com").ldif("classpath:users.ldif");
		 */
		
		/*
		 * 4.当使用nosql 型数据库时,可以自定义
		 * 	spring security 提供一个UserDetailService 接口,我们自定义的service 需要
		 * 	实现它的loadUserByUsername 方法。
		 */
		auth.userDetailsService(new UserServiceImpl(userDao));
	}

	/**
	 * 
	 */
	@Override
	public void configure(WebSecurity web) throws Exception {
		super.configure(web);
	}

	/**
	 * 	对每个请求进行细粒度安全性控制
	 * 	注意将最为具体的请求路径放在前面,不具体的放在后面(如anyRequest)
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		//input 中name=“remember-me”
		http.rememberMe()	//通过cookie 存储token(包含用户名,密码,过期时间,私匙且都md5哈希加密)
				.tokenValiditySeconds(2419200)	//有效时间
				.key("myKey")	//私匙
			.and()
			.logout()
				.logoutUrl("/logout")	//默认访问的登出路径
				.logoutSuccessUrl("/");	//登出重定向
		
		//匹配路径.如何保护
		http.formLogin()	//启用默认的登陆页面
				.loginPage("/login")	
			.and()
			.authorizeRequests()	//拦截请求
				.antMatchers("/user/me").access("hasRole('Role_USER') and hasIpAddress('192.168.1.2')")	//springEL表达式
				.regexMatchers("/user/.*").authenticated()	//认证:authenticated 表示必须已登陆
				.antMatchers(HttpMethod.POST, "/user").hasRole("USER")	//权限:自动加前缀ROLE_
				.anyRequest().permitAll()
			.and()
			.requiresChannel()	//强制通道的安全性
				.antMatchers("/user/insertUser").requiresSecure()	//要求https 安全通道
				.antMatchers("/").requiresInsecure();	//如果通过https 发送了对 / 的请求,会自动把请求重定向到不安全的http 通道上
		
		/*
		 * 	跨站请求伪造csrf:一个站点欺骗用户提交请求到其他服务器,默认开启。
		 * 	spring security 通过一个同步token 的方式来实现csrf 防护功能。
		 * 	它会拦截非get、head、options、trace 的请求并检查csrf token,不存在或不匹配则抛csrf 异常
		 * 	我们要在表单中 name="_csrf" 的input域中提交token
		 */
		http.csrf().disable();
	}
	
	
}

 

4、业务实现

package com.qhf.service;

import java.util.ArrayList;
import java.util.List;

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 com.qhf.dao.UserDao;
import com.qhf.entity.User;

public class UserServiceImpl implements UserDetailsService {

	UserDao userDao;
	
	public UserServiceImpl(UserDao userDao) {
		super();
		this.userDao = userDao;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userDao.selectByUsername(username);
		
		if(user != null) {
			List<GrantedAuthority> authorities = new ArrayList<>();
			authorities.add(new SimpleGrantedAuthority("ROLE_USER"));//添加权限列表
			
			return new org.springframework.security.core.userdetails
				.User(user.getUsername(), user.getPassword(), authorities);
		}
		
		throw new UsernameNotFoundException("User '" + username + " ' not found");
		
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值