SpringBoot——Spring Security 框架
作者:mmseoamin日期:2023-12-11

优质博文:IT-BLOG-CN

一、Spring Security 简介

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的 Bean,充分利用了Spring IoC,DI(控制反转 Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

二、Spring Security 入门Demo

【1】创建Maven工程(war形式):spring-security-demo

【2】修改pom.xml目录,如下:



  4.12
  4.2.4.RELEASE
  4.0.0
  2.5
  2.8.4
  3.4.7
  0.1		
  3.2.8
  1.2.2
  1.2.15
  5.1.32		
  1.0.9
  1.3.1
  2.3.23
  5.11.2
  3.2.3.RELEASE		
  4.10.3
  2012_u6		


	
		org.springframework
		spring-core
		${spring.version}
	
	
		org.springframework
		spring-web
		${spring.version}
	
	
		org.springframework
		spring-webmvc
		${spring.version}
	
	
		org.springframework
		spring-context-support
		${spring.version}
	
	
		org.springframework
		spring-test
		${spring.version}
	
	
		org.springframework
		spring-jdbc
		${spring.version}
	
	
		org.springframework.security
		spring-security-web
		4.1.0.RELEASE
	
	
		org.springframework.security
		spring-security-config
		4.1.0.RELEASE
	
	
		javax.servlet
		servlet-api
		2.5
		provided
	


	
		
		
			org.apache.maven.plugins
			maven-compiler-plugin
			3.2
			
				1.7
				1.7
				UTF-8
			
		
		
			org.apache.tomcat.maven
			tomcat7-maven-plugin
			
				
				9090
				
				/
			
		
	

注意:如果只是给自己的项目中嵌入Spring Security安全框架,只需要添加如下两个jar包即可。


	org.springframework.security
	spring-security-web
	4.1.0.RELEASE


	org.springframework.security
	spring-security-config
	4.1.0.RELEASE

【3】创建web.xml文件(通过Spring Security拦截需要处理的请求和引入Spring Security的配置文件)。


	
	
  	 
		contextConfigLocation
		classpath:spring-security.xml
	 
	 
		
			org.springframework.web.context.ContextLoaderListener
		
	 
	
	
	   
		springSecurityFilterChain  
		org.springframework.web.filter.DelegatingFilterProxy  
	   
	   
		springSecurityFilterChain  
		/*  
	 

【4】创建Spring Security配置文件:spring-security.xml(与 web.xml中引入的配置文件对应),代码中有配置的详细说明注解。




 
	
	
	
	
	
	
	
	
	
	
	
	    
		
		
		
		
		
		
		
			
		
		
		
	
	
	
	
	    
	  
	    	
		  		
		  			
		  		
		  	
	   
		
	
	
 
 
	
	

【5】需要自己创建:login.html(登录页面 如下)、index.html(登录成功跳转页面)、login_error.html(登录失败跳转页面)





登陆


    --欢迎登陆我的系统--
    
用户名:
密码:

三、项目实战中,后台业务逻辑实现重要代码摘取,供实战中使用

【1】配置文件中配置的,登录时用户名和密码的业务逻辑处理类(实现UserDetailsService:框架自带的接口),注意:普通demo不需要此部分,主要用于真是环境登录逻辑的处理。

public class UserDetailServiceImpl implements UserDetailsService {
	//当通过配置文件的形式,引入服务类时需要设置set方法。
	private SellerService sellerService;
	
	public void setSellerService(SellerService sellerService) {
		this.sellerService = sellerService;
	}
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		//GrantedAuthority : 定义用户角色,需要实现的接口
		List list =new ArrayList<>();
		//用户添加角色,SimpleGrantedAuthority可以定义用户角色,实现了GrantedAuthority接口,
                //格式必须以(ROLE_)开头
		list.add(new SimpleGrantedAuthority("ROLE_SELLER"));
		//从数据库中获取用户信息。
		TbSeller seller = sellerService.findOne(username);
		if(seller != null) {
			//返回username:传过来的参数。seller.getPassword:数据库中获取到的用户密码。
                        //list:用户的所有角色,可以从数据库中获取
			return new User(username, seller.getPassword(), list);
		}else {
			return null;
		}
	}
}

【2】BCrypt加密过程(配置中说明了BCrypt与MD5的区别)。

@RequestMapping("/add")
public Result add(@RequestBody TbSeller seller){
	try {
		//BCrypt加密使用的对象BCryptPasswordEncoder
		BCryptPasswordEncoder cryptPasswordEncoder = new BCryptPasswordEncoder();
		//使用encode方法对传入的密码加密,返回30位的字符串
		String encode = cryptPasswordEncoder.encode(seller.getPassword());
		//业务处理:接入对象中
		seller.setPassword(encode);
		//调用服务层,入库
		sellerService.add(seller);
		return new Result(true, "增加成功");
	} catch (Exception e) {
		e.printStackTrace();
		return new Result(false, "增加失败");
	}
}

四、当程序中需要用户名时,可通过 SecurityContextHolder 对象获取

☏ SecurityContextHolder用于存储安全上下文security context的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保存在SecurityContextHolder中。SecurityContextHolder默认使用ThreadLocal策略来存储认证信息。看到ThreadLocal也就意味着,这是一种与线程绑定的策略。Spring Security在用户登录时自动绑定认证信息到当前线程,在用户退出时,自动清除当前线程的认证信息。但这一切的前提,是你在web场景下使用Spring Security。

☏ 因为身份信息是与线程绑定的,所以可以在程序的任何地方使用静态方法获取用户信息。一个典型的获取当前登录用户的姓名的例子如下所示:getAuthentication() 返回了认证信息。

@RequestMapping("/name")
public Map getName(){
	//获取登录名
	String name = SecurityContextHolder.getContext().getAuthentication().getName();
	Map map = new HashMap<>();
	map.put("loginName", name);
	return map;
}

☏ Authentication源码:

package org.springframework.security.core;// <1>
public interface Authentication extends Principal, Serializable { // <1>
    Collection getAuthorities(); // <2>
    Object getCredentials();// <2>
    Object getDetails();// <2>
    Object getPrincipal();// <2>
    boolean isAuthenticated();// <2>
    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

【1】Authentication 是Spring Security包中的接口,直接继承自Principal类,而Principal是位于java.security包中的。可以见得,Authentication在Spring Security中是最高级别的身份/认证的抽象。

【2】由这个顶级接口,我们可以得到用户拥有的权限信息列表,密码,用户细节信息,用户身份信息,认证信息。

authentication.getPrincipal()返回了一个 Object,我们将Principal强转成了Spring Security中最常用的UserDetails,这在Spring Security 中非常常见,接口返回Object,使用instanceof判断类型,强转成对应的具体实现类。接口详细解读如下:

 ● getAuthorities():权限信息列表,默认是GrantedAuthority接口的一些实现类,通常是代表权限信息的一系列字符串。

 ● getCredentials():密码信息,用户输入的密码字符串,在认证过后通常会被移除,用于保障安全。

 ● getDetails():细节信息,web应用中的实现接口通常为WebAuthenticationDetails,它记录了访问者的ip地址和sessionId的值。

 ● getPrincipal():最重要的身份信息,大部分情况下返回的是UserDetails接口的实现类,也是框架中的常用接口之一。