SpringBoot权限管理和单点登录

  • 技术介绍
  • SpringSecurity简介
  • SpringSession简介
  • SpringSession背景
  • SpringSession介绍
  • SpringSession工作原理
  • 代码实现
  • 创建工程
  • Application配置
  • Security配置代码
  • Controller代码
  • 引入 Nginx
  • 总结


技术介绍

这里运用到的技术有 SpringBoot + SpringSecurity + SpringSession + Redis,下面跟大家一起去了解一下每个框架的作用。

SpringSecurity简介

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。它的核心是一组过滤器链,不同的功能经由不同的过滤器。


#### spring security的核心功能 - 认证 (你是谁) - 授权 (你能干什么) - 攻击防护 (防止伪造身份)

spring boot sso OAuth 单点登录 springboot整合单点登录_spring


SpringSession简介

SpringSession背景

http session(企业)一直都是我们做集群时需要解决的一个难题,我们知道HttpSession是通过Servlet容器创建和管理的,像Tomcat/Jetty都是保存在内存中的。而如果我们把web服务器搭建成分布式的集群,然后利用LVS或Nginx做负载均衡,那么来自同一用户的Http请求将有可能被分发到两个不同的web站点中去。那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢?
最简单的想法就是把session数据保存到内存以外的一个统一的地方,例如Memcached/Redis等数据库中。那么问题又来了,如何替换掉Servlet容器创建和管理HttpSession的实现呢?

  1. 设计一个Filter,利用HttpServletRequestWrapper,实现自己的 getSession()方法,接管创建和管理Session数据的工作。spring-session就是通过这样的思路实现的。
  2. 利用Servlet容器提供的插件功能,自定义HttpSession的创建和管理策略,并通过配置的方式替换掉默认的策略。不过这种方式有个缺点,就是需要耦合Tomcat/Jetty等Servlet容器的代码
  3. 或者通过nginx之类的负载均衡做ip_hash,路由到特定的服务器上。 此策略会出现单点故障问题。

SpringSession介绍

Spring Session是Spring的项目之一,专注于解决session管理问题。Spring Session提供了集群Session功能,默认采用外置的Redis来存储Session数据,以此来解决Session共享的问题。
spring-session提供对用户session管理的一系列api和实现。提供了很多可扩展、透明的封装方式用于管理httpSession/WebSocket的处理。

SpringSession工作原理

spring boot sso OAuth 单点登录 springboot整合单点登录_nginx_02


代码实现

创建工程

首先 创建一个 Spring Boot 工程,引入 Web、Spring Session、Spring Security 以及 Redis

spring boot sso OAuth 单点登录 springboot整合单点登录_nginx_03


spring boot sso OAuth 单点登录 springboot整合单点登录_nginx_04


创建成功之后,pom.xml 文件如下

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>
	</dependencies>

Application配置

spring.redis.host=127.0.0.1
spring.redis.port=6379

spring.security.user.name=user
spring.security.user.password=test

server.port=8080

配置一下 Redis 的基本信息;Spring Security 为了简化,我就将用户名密码直接配置在 application.properties 中了,最后再配置一下项目端口号。


Security配置代码

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
        http.formLogin()
        	 .and()
        	 .authorizeRequests() // 允许放行
//             .antMatchers("/set").permitAll()  
//             .antMatchers("/get").permitAll() 
             .anyRequest().authenticated()  // 所有请求需要身份认证
             .and()
             .cors()  //支持跨域
             .and()
             .logout().logoutUrl("/logout")  //默认就是"/logout"
             .and()
             .sessionManagement()
             .invalidSessionUrl("/invalid");  // 设置session过期 跳转页面
	}
	
	/**
	 *  该方法是登录的时候会进入
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		 // 使用自定义身份验证组件
        auth.authenticationProvider(new SecurityAuthentication(userDetailsService));
	}	
}

Controller代码

@RestController
public class LoginCtrl {

	@Value("${server.port}")
	Integer port;

	@GetMapping("/set")
	public String set(HttpSession session) {
		session.setAttribute("user", "java");
		return String.valueOf(port);
	}

	@GetMapping("/get")
	public String get(HttpSession session) {
		return session.getAttribute("user") + ":" + port;
	}
	
	@GetMapping("/logout")
	public  String  logout(HttpSession session) {
		session.invalidate();
		return "退出登录";
	}
	
	@RequestMapping("/invalid")
	public String invalid() {
	    return "Session 已过期,请重新登录";
	}
}

配置完成后 ,就可以使用 Spring Session 了,其实就是使用普通的 HttpSession ,其他的 Session 同步到 Redis 等操作,框架已经自动帮你完成了


引入 Nginx

很简单,进入 Nginx 的安装目录的 conf 目录下(默认是在 /usr/local/nginx/conf),编辑 nginx.conf 文件:

spring boot sso OAuth 单点登录 springboot整合单点登录_spring_05


在这段配置中:

  1. upstream 表示配置上游服务器
  2. javaboy.org 表示服务器集群的名字,这个可以随意取名字
  3. upstream 里边配置的是一个个的单独服务
  4. weight 表示服务的权重,意味者将有多少比例的请求从 Nginx 上转发到该服务上
  5. location 中的 proxy_pass 表示请求转发的地址,/ 表示拦截到所有的请求,转发转发到刚刚配置好的服务集群中
  6. proxy_redirect 表示设置当发生重定向请求时,nginx 自动修正响应头数据(默认是 Tomcat 返回重定向,此时重定向的地址是 Tomcat 的地址,我们需要将之修改使之成为 Nginx 的地址)

总结

如果大家没有在 SSM 架构中用过 Spring Session ,可能不太好理解我们在 Spring Boot 中使用 Spring Session 有多么方便,因为在 SSM 架构中,Spring Session 的使用要配置三个地方 ,一个是 web.xml 配置代理过滤器,然后在 Spring 容器中配置 Redis,最后再配置 Spring Session,步骤还是有些繁琐的,而 Spring Boot 中直接帮我们省去了这些繁琐的步骤!

用SpringBoot大概的整合了一下 SpringSecurity 和 SpringSession 。达到了 单点登录 Session共享 权限限制 等想要的目的。虽然本文较长,但是实际上的配置没啥,涉及到的配置也都很简单,方便大家直接运用。