一、 基本配置

配置在系统中使用SpringSecurity,需要在pom.xml中加入spring-boot-starter-security依赖

代码如下

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后新建一个类继承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter类,用于实现SpringSecurity配置

package com.client;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


@Configuration
public class TestSecurity extends WebSecurityConfigurerAdapter{

	
}

这样在我们项目的中就启用了最基本的Security配置,这时启动时会在控制台中打印当前用户的密码信息。如:

Using generated security password: c0b0604b-650c-42a1-b430-566dc0a6b77c

这是我这里打印的,这时当我们访问相关的接口时会自动跳转到如下界面:

springsecurity设置失效时间 springsecurity configure_ide

输入用户名:user 和控制台输出的密码即可登录,我这里的密码为:c0b0604b-650c-42a1-b430-566dc0a6b77c

登录后就可以自由访问相关的接口信息了

另外由于SpringSecurity会将用户信息存储到Cookie和SecurityContextHolder对象中因此我们在登出里需要消除Cookie信息和SecurityContextHolder信息,当然我们也可以在任意地方通过SecurityContextHolder.getContext().getAuthentication().getPrincipal()取得当前登录的用户信息。 如:

Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User)obj;
		System.out.println(user.getPassword());
		System.out.println(user.getUsername());
		System.out.println(user.getAuthorities());

二、自定义配置

在很多情况下我们需要自定义自己的用户名密码和访问权限信息,这时我们就需要对前面说过的org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter类进行复写相关的接口用于我们自己的信息

配置1:自定义用户信息和密码信息

由于SpringSecurity要求密码必须进行加密,因此我们需要先设置一个加密方式,设置方式有两种

第一种,直接注入一个加密方式,如下面代码中,即注入了密码加密方式,并设置用户为admin,密码为admin,同时admin具有admin角色:

package com.client;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class TestSecurity extends WebSecurityConfigurerAdapter {
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin");
	}
}

第二种方式即直接在代码中设置密码加密方式, 下面的代码实现的功能和上面的代码实现功能相同

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
		.withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin");
	}

在没有给角色设置任何权限的情况下,默认为角色拥有所有权限,如以下代码,角色admin和角色user权限相同

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
		.withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin")
		.and().withUser("user").password(new BCryptPasswordEncoder().encode("user")).roles("user");
	}

 

配置2:给自定义角色设置权限信息

既然我们给用户设置了角色,那么我们就需要给对应的角色配置相关的权限,如可以访问哪个接口不能访问哪个接口等。设置角色我们需要复写另外一个configure方法。代码如下:

@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().antMatchers("/test/**").hasRole("user").antMatchers("/**").hasRole("admin")
				.anyRequest().authenticated().and().formLogin().and().httpBasic();

	}

在这个代码中配置了user角色只能访问 test路径下面的所有接口,而admin可以访问所有接口;

注意:这里有一个顺序的问题,因此/test/**包含的/**内,所以一定先配置范围小的,然后再配置范围大,否则就会造成配置失效的问题,另外也不能配置重复路由。如/test/**已经配置了,如果再配置一个同样也会造成无法访问的问题

springsecurity设置失效时间 springsecurity configure_spring_02

在这个代码中由于/test/**这个权限给user和test都进行了配置,所有会造成user角色的无效,而test角色有效。因此要尽量避免这种问题的发生。如果需要配置多个角色可以用hasAnyRole方式配置多个角色,代码如下:

@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.antMatchers("/test/**").hasAnyRole("admin","user","test")
		.antMatchers("/**").hasRole("admin")
				.anyRequest().authenticated().and().formLogin().and().httpBasic();

	}

过滤请求配置

有一些接口我们如果不想进行权限认证可以通过下面的方式设置。如:下面的代码即访问/test/show接口时不需要进行用户认证

@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/test/show");
	}

自定义用户认证配置

上面的用户登录功能在我们的真正的项目中使用是比较少的。因此不论是什么样的系统,用户的信息一般都是在数据库中进行存储着,这样我们就需要通过数据来进行用户及相关密码的验证,因此我们需要将用户的验证信息改为数据库验证。在这里我们需要用到org.springframework.security.core.userdetails.UserDetailsService类。

第一步,我先创建一个类,继承org.springframework.security.core.userdetails.UserDetailsService类并复写loadUserByUsername方法。代码如下:

package com.client;

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

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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;


@Service
public class UserDetailsServiceImpl implements UserDetailsService{

	@Autowired
	private PasswordEncoder encode;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		return null;
	}
	

}

 在这个方法中,username即是当前登录用户。而encode则是密码的加密算法

 第二步:设置当前用户的信息用于验证登录。修改上面的代码,查询数据库取得用户信息及权限信息

@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
		//用户的角色信息 通过数据库查询获得,我这里做一个角色模拟,就不再进行数据库查询了
		authList.add(new SimpleGrantedAuthority("ROLE_user"));      
		authList.add(new SimpleGrantedAuthority("ROLE_admin"));
		//根据用户的登录名到数据库中查询相关的用户信息;我这里模拟一个用户,就不做数据库查询了 new User("admin", encode.encode("123456"),authList);
		return new User("admin", encode.encode("123456"),authList);
	}

第三步:修改Security配置信息,这里是修改上面已经写了的TestSecurity类 。即需要设置用户的处理服务。 代码如下:

package com.client;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class TestSecurity extends WebSecurityConfigurerAdapter {

	@Autowired
	private UserDetailsServiceImpl impl;
	
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		
		auth.userDetailsService(impl);
				
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
				 .formLogin()

				.and()
				.authorizeRequests().antMatchers("/test/**").hasAnyRole("admin", "user", "test")
				.antMatchers("/**").hasRole("admin")
				.anyRequest().authenticated().and().httpBasic();

	}

	@Override
	public void configure(WebSecurity web) throws Exception {
	}
}

 

三、http.authorizeRequests()重点参数说明

1、anyRequest 匹配所有的URL;如代码中的.anyRequest().authenticated()  即所有请求都需要进行登录认证

2、formLogin() 登录表单配置,设置formLogin后,才会显示对应的登录界面,否则会弹出对应的对话框要求输入用户名密码。

3、httpBasic() 认证方式

4、.permitAll() 不需要认证即可以访问的资源设置

5、withObjectPostProcessor()  后置处理程序

四、WebSecurityConfigurerAdapter类说明

这个类的全名为:org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter。我们可以通过复写这个类里面的相关方法,来对权限认识对应相关的操作,这个类里面可以复写的主要方法有下面几个:

protected void configure(AuthenticationManagerBuilder auth) throws Exception;
public void configure(WebSecurity web) throws Exception;
protected void configure(HttpSecurity http) throws Exception;

public AuthenticationManager authenticationManagerBean() throws Exception;
protected AuthenticationManager authenticationManager() throws Exception;
public UserDetailsService userDetailsServiceBean() throws Exception
protected UserDetailsService userDetailsService();

其中 AuthenticationManagerBuilder 主要是用于创建AuthenticationManager(认证管理器),可以通过AuthenticationManager进行内存身份验证、LDAP身份验证、基于数据库的身份验证等,AuthenticationManagerBuilder 也可以设置当前系统的用户信息或增加UserDetailsService、AuthenticationProvider等。。如以下代码就是设置当前的内存用户信息

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin")
		.and().withUser("user").password(new BCryptPasswordEncoder().encode("user")).roles("user")
		.and().withUser("test").password(new BCryptPasswordEncoder().encode("test")).roles("test");
		
	}

AuthenticationManagerBuilder中的authenticationEventPublisher方法则是定义了Spring Security中用户授权成功或失败的通知机制。它接收一个AuthenticationEventPublisher接口。

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	
		auth.authenticationEventPublisher(new AuthenticationEventPublisher() {
			
			@Override
			public void publishAuthenticationSuccess(Authentication authentication) {
				// 成功后的通知
				System.out.println("success");
			}
			
			@Override
			public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
				// 失败后的通知
				System.out.println("failure");
			}
		});
	}

 AuthenticationManagerBuilder中的authenticationProvider方法则是定义了Spring Security中的认证机制。它接收一个AuthenticationProvider接口,通过这个接口来自定义登录信息。这个接口中提供了两个方法。其中supports方法用于判断支持哪个验证,authenticate方法用于执行验证。

@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		
		auth.authenticationProvider(new AuthenticationProvider() {
			
			//支持哪种验证
			@Override
			public boolean supports(Class<?> authentication) {
				//如果是支持用户名密码的验证可以这样写
				return authentication.equals(UsernamePasswordAuthenticationToken.class);
//				return false;
			}
			
			//验证方式
			@Override
			public Authentication authenticate(Authentication authentication) throws AuthenticationException {
				//取得密码
				String pwd = authentication.getCredentials().toString();
				//取得用户名
				String uname = authentication.getPrincipal().toString();
				if(/*自定义验证通过-如进行数据库验证或LDAP验证等*/true) {
					return new UsernamePasswordAuthenticationToken(uname, pwd);	
				}
				
				//如果验证不通过
				return null;
			}
		});
	}

 Spring Security中进行身份验证的是AuthenticationManager接口,ProviderManager是它的一个默认实现,但它并不用来处理身份认证,而是委托给配置好的AuthenticationProvider,每个AuthenticationProvider会轮流检查身份认证。检查后如果成功返回Authentication对象失败则抛出异常。而验证身份其实就是调用userDetailsService。因此我们进行身份认证时多采用实现UserDetailsService接口的方式来进行处理。这个接口需要返回一个用户信息对象UserDetails,里面包含用户名密码及权限信息。AuthenticationProvider会通过UserDetails这个对象和Authentication对象进行比较验证。

springsecurity设置失效时间 springsecurity configure_spring_03

WebSecurity 

WebSecuritySpring Security的一个SecurityBuilder。它的任务是基于一组WebSecurityConfigurer构建出一个Servlet Filter,具体来讲就是构建一个Spring SecurityFilterChainProxy实例。这个FilterChainProxy实现了Filter接口,也是通常我们所说的Spring Security Filter Chain,所构建的FilterChainProxy实例缺省会使用名称springSecurityFilterChain作为bean注册到容器,运行时处理web请求过程中会使用该bean进行安全控制。每个FilterChainProxy包装了一个HttpFirewall和若干个SecurityFilterChain, 这里每个 SecurityFilterChain要么对应于一个要忽略安全控制的URL通配符(RequestMatcher);要么对应于一个要进行安全控制的URL通配符(HttpSecurity)

@Override
		public void configure(WebSecurity web) throws Exception {
			web.ignoring().antMatchers("/test/sss");
		}

HttpSecurity

 HttpSecurity是主要是权限认证的信息配置。其中openidLogin()用于配置基于openId的认证,headers()用于将安全标头添加到响应,cors()用于配置跨域资源共享,sessionManagement()用于会话管理,portMapper()允许配置一个PortMapper,其他提供SecurityConfigure的对象使用PortMapper从Http重定向到Https或从Https重定向到Http。默认情况下,Spring Security使用一个PortMapperImpl映射Http端口8080到Https端口8443,Http端口80到Https商品443。

jee()配置基于容器的认领,这种情况下认证由Sevlet容器管理。

x509()配置基于x509的认证。

rememberMe()用于配置记住我的认证。

authorizeRequests()基于HttpServletRequests的访问限制认证。

requestCache() 允许配置请求缓存

exceptionHandling() 允许配置错误处理

securityContext() 在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用

servletApi() 将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用

csrf() 添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用

logout() 添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success

anonymous()  允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”

formLogin() 指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面

oauth2Login()  根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证

requiresChannel() 配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射

httpBasic() 配置 Http Basic 验证

addFilter(filter) 添加过滤器

在HttpSecurity中authenticated认证会有权限的认证,而permitAll则没有权限的认证,只需要用户登录了即可,可以通过permitAll来过滤一般的静态界面请求。