springboot+serurity+oauth2实战
1. SpringBoot 2.3版本示例 1.1 基础介绍 OAuth2是什么?4中基本授权模式理解:
Sercurity是什么?:
Sercurity + OAuth2:
springcloud + 前后端分离的模式中 主要使用密码模式
和授权码模式
,此文章以密码模式
介绍
作用与分工:
OAuth2主要用于校验客户端合法性、产生token、校验token Sercurity主要用于用户名密码校验、接口权限控制
OAuth2与Sercurity整合之后,校验顺序: 获取token请求/oauth/token
:校验客户端合法性——校验用户名密码——产生token 受保护的接口请求/**
:校验token——校验接口权限
授权接口流程:
源码解析:
1.2 主要架构tree: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ├── auth # 授权服务 │ ├── pom.xml │ └── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── yuzhe │ │ │ ├── AuthApplication.java │ │ │ ├── config │ │ │ │ ├── AuthorizationServerConfig.java # 授权服务器器配置 │ │ │ │ └── SecurityConfig.java # SpringSecurity 相关配置 │ │ │ └── controller │ │ │ └── AuthController.java # 退出登录接口 │ │ └── resources │ │ └── application.yaml │ └── test │ └── java ├── pom.xml ├── service-a # 资源服务 │ ├── pom.xml │ └── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── yuzhe │ │ │ ├── AppApplication.java │ │ │ ├── Filter │ │ │ │ ├── SkipAuthenticationFilter.java # 自定义过滤器 │ │ │ │ └── SkipTokenForSpecificUserFilter.java │ │ │ ├── config │ │ │ │ └── ResourceServerConfig.java # 资源服务器配置 │ │ │ └── controller │ │ │ └── HelloController.java # 资源接口 │ │ └── resources │ │ └── application.yaml │ └── test │ └── java └── yuzhe-security-oauth2-password-springboot2.3.iml
1.3 授权服务 auth 1.3.0 Maven依赖 spring cloud自从2020.0.0(含)以上版本就移除了spring-cloud-security-dependencies依赖,所以从2020.0.0版本开始,无法引入spring-cloud-starter-oauth2,oauth2授权服务分离为一个独立的project:spring authorization server
本示例使用 springboot 2.3 spring cloud Hoxton.SR11, 所以可以使用spring-cloud-starter-oauth2
1 2 3 4 5 6 7 8 9 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-oauth2</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency >
1.3.1 授权服务器器配置 授权服务器器配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 package com.yuzhe.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.annotation.Resource;@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Resource private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore tokenStore () { RedisTokenStore redisTokenStore = new RedisTokenStore (redisConnectionFactory); redisTokenStore.setPrefix("ZYZ-TOKEN:" ); return redisTokenStore; } @Autowired private PasswordEncoder passwordEncoder; @Override public void configure (ClientDetailsServiceConfigurer clients) throws Exception { clients .inMemory() .withClient("client" ) .secret(passwordEncoder.encode("secret" )) .authorizedGrantTypes("password" , "refresh_token" ) .scopes("read" , "write" ) .accessTokenValiditySeconds(360 ) .refreshTokenValiditySeconds(720 ) .resourceIds("service-a" , "service-b" ) ; } @Override public void configure (AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .authenticationManager(authenticationManager) .tokenStore(tokenStore()); } }
主要组件:
客户端
相关配置 ClientDetailsService组件
InMemoryClientDetailsService:将客户端信息存储在内存中。
JdbcClientDetailsService:将客户端信息存储在数据库中
端点
配置 TokenEndpoint组件
主要配置授权模式,tokenStore,tokenEnhancer等
TokenStore
组件
InMemoryTokenStore:将令牌存储在内存中。
JdbcTokenStore:将令牌存储在数据库中。
JwtTokenStore:使用 JWT 格式存储令牌。
RedisTokenStore: 使用Redis存储令牌。
常用JwtTokenStore和RedisTokenStore,
JwtTokenStore 授权时 授权服务器将用户信息、权限、过期时间等存到token中,故而token会很长,资源服务器直接解析token中信息做校验
RedisTokenStore 授权时 授权服务器将用户信息、权限、过期时间存到redis,token只有很短的一个字符串,资源服务器校验时用此字符串从redis中获取信息做校验
微服务中最好使用RedisTokenStore
,因为RedisTokenStore是有状态的,代码可以控制token的失效和删除(用来做 登出 操作),但是JwtTokenStore是无状态的,无法删除和失效,只有过期时间结束了,此token才会失效
授权接口:
curl -X POST -u client:secret -d “grant_type=password&username=user&password=password” http://localhost:17700/oauth/token
1.3.2 WebSecurity配置 WebSecurity配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.yuzhe.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.AuthenticationManager;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.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder () { return new BCryptPasswordEncoder (); } @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure (AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("user" ) .password(passwordEncoder.encode("password" )) .roles("USER" ); } @Override @Bean public AuthenticationManager authenticationManagerBean () throws Exception { return super .authenticationManagerBean(); } @Override protected void configure (HttpSecurity http) throws Exception { http .csrf().disable() .formLogin().disable() .httpBasic().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/oauth/token" ).permitAll() .antMatchers("/oauth/logout" ).permitAll() .anyRequest().authenticated() ; } }
AuthenticationManagerBuilder 实际项目中是使用自定义userDetailsService
a 1 2 // auth.userDetailsService(userDetailsService) // .passwordEncoder(passwordEncoder);
从数据库中读取 帐号、密码、角色、 权限,这里userDetailsService相关的代码不贴了
1.3.1 自定义授权模式 1.4 资源服务 service-a 1.4.0 Maven依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-oauth2</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-oauth2-resource-server</artifactId > </dependency >
1.4.1 资源服务器配置 ResourceServe配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package com.yuzhe.config;import com.yuzhe.Filter.SkipAuthenticationFilter;import com.yuzhe.Filter.SkipTokenForSpecificUserFilter;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.annotation.Resource;@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure (ResourceServerSecurityConfigurer resources) { resources.resourceId("service-a" ); } @Override public void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/public" ).permitAll() .anyRequest().authenticated() ; } @Resource private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore tokenStore () { RedisTokenStore redisTokenStore = new RedisTokenStore (redisConnectionFactory); redisTokenStore.setPrefix("ZYZ-TOKEN:" ); return redisTokenStore; } }
1.4.2 自定义过滤器 过滤器链:
todo。。。
1.5 对接外部SSO todo。。
1.6 使用oauth2开发SSO todo。。
2. SpringBoot 2.6版本示例 3. SpringBoot 3 版本示例