概述:
一.什么是SSO单点登录登录
我理解是通常微服务项目中有多个子系统(一个功能模块对应一个子系统),当用户在一个子系统登录之后,如果用户再切换其他的子系统那不可能再让用户进行登录操作吧,如果每次切换都要用户重新登录那么就会极大影响用户的体验,所以我们就需要sso单点登录系统来做统一的登录授权操作,一次授权处处信任,不管你的微服务有多少个子服务只要成功登录过一次,那么其他系统就不需要重新进行登录操作用户照样可以进行访问
二.什么是授权码模式
三.搭建SSO单点登录系统
1.建一个springsecurity-oauth2maven项目,里面有三个springsecurity-sso模块代表三个子系统
2.引入相关的pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>springsecurity-sso</module>
<module>springsecurity-sso2</module>
<module>springsecurity-sso3</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.13.RELEASE</version>
<relativePath/>
</parent>
<groupId>org.example</groupId>
<artifactId>springsecurity-oauth2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring.cloud.version>Greenwich.SR2</spring.cloud.version>
</properties>
<dependencies>
<!-- spring-cloud-starter-oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!-- spring-cloud-starter-security -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!--web组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- spring-boot-starter-data-redis -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>-->
<!-- commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<!-- jwt组件 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.首先编写启动类SpringSecurityOauthApp
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringSecurityOauthApp {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityOauthApp.class);
}
}
4.实现自定义的登录逻辑,不使用springsecurity自带的登录逻辑
5.springsecurity配置,包括一些登录url,拦截哪些请求,密码加密器,授权管理器,关闭csrf防护,哪些请求需要放行之类的,详细的配置如下
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**","/login/**","/logout/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
/*
* 密码加密器
* */
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/*
* 授权管理器
* */
@Override
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
6.认证授权服务器配置,主要是对于第三方客户端配置,秘钥,授权成功之后的重定向url ,授权范围,令牌刷新时间,令牌失效时间,授权类型,是否自动授权等信息,注意记得加上@EnableAuthorizationServer这个注解(很重要)
public class AuthorizeServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
/*
* 这个客户端代表着访问我们授权服务器的第三方应用程序
* */
//指定令牌的存储策略为redi或inMemory内存
clients.inMemory()
//客户端id
.withClient("client")
//秘钥
.secret(passwordEncoder.encode("ihyx"))
//重定向地址
// .redirectUris("http://www.baidu.com")
.redirectUris("http://localhost:8081/login","http://localhost:8082/login","http://localhost:8083/login") //sso单点登录系统演示
//授权范围
.scopes("all")
//设置访问令牌失效时间
.accessTokenValiditySeconds(60)
//设置刷新令牌失效时间
.refreshTokenValiditySeconds(1200)
//自动授权
.autoApprove(true)
/*
* 授权类型
* authorization_code:授权码类型
* password:密码类型
* refresh_token:刷新令牌
*
* */
.authorizedGrantTypes("authorization_code","password","refresh_token");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService)
//指定令牌存储策略
//.tokenStore(tokenStore)
//使用jwt令牌存储策略
.tokenStore(tokenStore)
//accessToken转成jwtToken
.accessTokenConverter(jwtAccessTokenConverter);
}
//SSO单点登录系统额外配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//获取秘钥必须要身份验证 单点登录必须要配置
security.tokenKeyAccess("isAuthenticated()");
}
7.资源服务器配置,配置需要放行的资源,注意加上@EnableResourceServer注解,目前该资源服务器上只有一个请求当前用户信息的请求表示一个资源
@Configuration
@EnableResourceServer
/*
* 资源服务器配置
* */
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/*
* 配置资源放行规则
* */
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/user/**");
}
}
@RestController
@RequestMapping("/user")
public class UserController {
/*
* 获取当前用户信息
* */
@RequestMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication, HttpServletRequest request){
//return authentication.getPrincipal();
String header = request.getHeader("Authorization");
//获取jwtToken
String token = header.substring(header.lastIndexOf("bearer") + 7);
//解析JwtToken
Object ihyx = Jwts.parser().
//设置秘钥
setSigningKey("ihyx".getBytes(Charset.forName("utf8")))
.parse(token).getBody();
return ihyx;
}
}
8.配置jwt令牌存储机制
@Configuration
public class JwtStoreConfig {
@Bean
public TokenStore tokenStore(){
TokenStore tokenStore=new JwtTokenStore(jwtAccessTokenConverter());
return tokenStore;
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//设置jwt秘钥
jwtAccessTokenConverter.setSigningKey("ihyx");
return jwtAccessTokenConverter;
}
}
9.子系统搭建,配置授权服务器地址以及获取token,授权码的url,然后子系统就一个获取当前用户信息的请求,必须登录之后才能请求否则的话就跳转到单点登录系统进行登录,其他的两个也是这样配置只是cookie-name不一样以及server.port
server.port=8082
#防止cookie冲突
server.servlet.session.cookie.name=springsecurity-sso-session02
#授权服务器地址
oauth.server.url=http://localhost:8080
#与授权服务器对应的配置
#客户端id
security.oauth2.client.client-id=client
#秘钥
security.oauth2.client.client-secret=ihyx
#认证url 获取授权码
security.oauth2.client.user-authorization-uri=${oauth.server.url}/oauth/authorize
#获取token
security.oauth2.client.access-token-uri=${oauth.server.url}/oauth/token
#获取jwt令牌
security.oauth2.resource.jwt.key-uri=${oauth.server.url}/oauth/token_key
@RestController
@RequestMapping("/app")
public class AppController {
@RequestMapping("/getUserInfo")
public Object getUserInfo(Authentication authentication){
return authentication.getPrincipal();
}
}
四.验证单点登录是否符合预期
1.启动四个project
2.依次访问localhost:8081/app/getUserInfo,localhost:8082/app/getUserInfo,localhost:8083/app/getUserInfo
3.在系统1登录,然后依次再请求localhost:8081/app/getUserInfo,localhost:8082/app/getUserInfo,localhost:8083/app/getUserInfo