当前位置: 代码迷 >> 综合 >> SpringSecurity系列文章 (六)Spring Security Oauth2
  详细解决方案

SpringSecurity系列文章 (六)Spring Security Oauth2

热度:50   发布时间:2023-11-01 12:58:24.0

目录

  • Spring Security Oauth2
    • 授权服务器
    • Spring Security Oauth2
      • 授权码模式:
        • pom.xml
        • java代码:
      • 密码模式
        • pom.xml
        • java 代码
      • 将token存到redis

Spring Security Oauth2

授权服务器

BUFxOO.png

  • Authorize Endpoint : 授权端点,进行授权
  • Token endpoint :令牌端点,进行授权拿到对应的token
  • Introspection Endpoint :校验端点,校验token
  • Revocation Endpoint : 撤销端点,撤销授权

Spring Security Oauth2

BUAdr8.png

流程:

1.用户访问,此时,没有token , Oauth2RestTemplate会出错,这个报错信息将会被Oauth2ClientContextFilter 捕获并重定向到认证服务器。

2.认证服务器通过Authorization endpoint 进行授权,并通过AuthhorizationServerTokenServices生成授权码并返回给客户端

3.客户端拿到授权码去认证服务器通过Token endpoint 调用AuthorizationServerTokenServices生成Token并返回给客户端

4.客户端拿到Token 去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验,校验通过后可以获取到资源

授权码模式:

效果是访问http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uti=http://www.baidu.com 跳到百度页面并且带授权码,并获取token

先看效果图

在这里插入图片描述

在这里插入图片描述

通过postman获取token:
在这里插入图片描述

在这里插入图片描述

通过token获取到资源,比如获取当前的用户。

BaSwjg.png

当我们把令牌给修改了就会报错。未认证。

看代码:

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.kaysanshi</groupId><artifactId>spring-security-oauth2-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-security-oauth2-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><spring-cloud.version>Greenwich.SR2</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></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>

java代码:

/*** 使用授权码模式*/
@SpringBootApplication
public class SpringSecurityOauth2DemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(SpringSecurityOauth2DemoApplication.class, args);}}

 配置实现类 /

/*** Description:* spring security配置* WebSecurityConfigurerAdapter是默认情况下 Spring security的http配置* 优先级高于ResourceServerConfigurer,用于保护oauth相关的endpoints,同时主要作用于用户的登录(form login,Basic auth)*@EnableWebSecurity :表示启动webSecurity* @date:2020/10/29 9:41* @author: kaysanshi**/
@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {
    @BeanPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();}/*** 进行** @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().authorizeRequests().antMatchers("/oauth/**", "/login/**", "logout/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll();}
}
//*** Description: 授权服务器的配置* author2配置* AuthorizationServerConfigurerAdapter 包括:* ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。* AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.* AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。** @date:2020/10/29 9:30* @author: kaysanshi**/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
    /*** 配置密码加密,因为再UserDetailsService是依赖与这个类的。** @return*/// 指定密码的加密方式@Autowiredprivate PasswordEncoder passwordEncode;/*** ClientDetailsServiceConfigurer* 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。* 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。* 演示提供了用户名和密码* @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients)throws Exception {
    // 配置一个客户端用于password认证的。同时也可以同时配置两个,可以再配置一个基于client认证的。clients.inMemory()// 配置clientId.withClient("admin")// 配置client-secret.secret(passwordEncode.encode("112233"))// 配置token过期时间.accessTokenValiditySeconds(2630)// 配置 redirectUri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type 表示授权类型。 使用授权码模式.authorizedGrantTypes("authorization_code");}
}
/
/*** Description:* 配置资源服务器 : ResourceServerConfigurerAdapter* ResourceServerConfigurerAdapter是默认情况下spring security oauth 的http配置。** @date:2020/10/29 9:44* @author: kaysanshi**/
@Configuration
@EnableResourceServer
public class ResourceServerConfigure extends ResourceServerConfigurerAdapter {
    /*** 配置响应资源的访问。** @param http* @throws Exception*/// 配置 URL 访问权限@Overridepublic void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers("/test/**");}
}

//////////// user类  ///////////////////////
////////////////////////////////////////////
/*** 自定义User 实现时一定要返回true 否则不能进行认证* @Author kay三石* @date:2020/10/31*/
public class User  implements UserDetails {
    private  String username;private  String password;private List<GrantedAuthority> authorities;public User(String username, String password, List <GrantedAuthority> authorities) {
    this.username = username;this.password = password;this.authorities = authorities;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {
    return null;}@Overridepublic String getPassword() {
    return password;}@Overridepublic String getUsername() {
    return username;}// 自动生成时是返回的为false,会导致不能进入@Overridepublic boolean isAccountNonExpired() {
    return true;}// 自动生成时是返回的为false,会导致不能进入@Overridepublic boolean isAccountNonLocked() {
    return true;}// 自动生成时是返回的为false,会导致不能进入@Overridepublic boolean isCredentialsNonExpired() {
    return true;}	// 自动生成时是返回的为false,会导致不能进入@Overridepublic boolean isEnabled() {
    return true;}
}

 userService实现类 /

/*** @Author kay三石* @date:2020/10/31*/
@Service
public class UserService implements UserDetailsService {
    /*** 注入刚引入的bean*/@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    String password =passwordEncoder.encode("123");return new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));}
}

 controller ///

/*** Description:* 加入了author2的认证,所以要加入token访问资源时* @date:2020/10/29 9:46* @author: kaysanshi**/
@RestController
@RequestMapping("/test")
public class TestController {
    /*** 获取当前用户* @return*/@GetMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication){
    return authentication.getPrincipal();}
}

遇到问题,但是后台并未报错,只需要把这个给修改即可 : 用户已失效,用户账户已经锁定

BUjSMj.png

解决:明明用户名和密码正确,而且没有设置状态锁定,怎么被锁定了呢?这是由于我们在重写UserDetails接口时,有个默认实现的方法public boolean isAccountNonLocked(),默认返回的是false

 @Overridepublic boolean isAccountNonExpired() {
    return true;}@Overridepublic boolean isAccountNonLocked() {
    return true;}@Overridepublic boolean isCredentialsNonExpired() {
    return true;}@Overridepublic boolean isEnabled() {
    return true;}

密码模式

使用密码模式获取资源:

看效果:

获取token

BaiSQf.png

通过token获取信息

Baipy8.png

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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.kaysanshi</groupId><artifactId>spring-security-oauth2</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-security-oauth2</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><spring-cloud.version>Greenwich.SR2</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></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>

java 代码

由于这个只是配置类中不太一样,这里我只展现出配置类的代码:

/*** Description:* author2配置* AuthorizationServerConfigurerAdapter 包括:* ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。* AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.* AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。** @date:2020/10/29 9:30* @author: kaysanshi**/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
    // 密码授权的操作就是通过这个对象把密码传入授权服务器的@Autowiredprivate AuthenticationManager authenticationManager;// 将令牌信息存储到内存中@Autowired(required = false)private TokenStore inMemoryTokenStore;// 该对象将为刷新token提供支持@Autowiredprivate UserService userService;@Autowiredprivate PasswordEncoder passwordEncoder;/*** ClientDetailsServiceConfigurer* 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。* 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients)throws Exception {
    // 配置一个基于password认证的。clients.inMemory()// 配置clientId.withClient("admin")// 配置client-secret.secret(passwordEncoder.encode("112233"))// 配置token过期时间.accessTokenValiditySeconds(2630)// 配置 redirectUri,用于授权成功后跳转.redirectUris("http://www.baidu.com")// 配置申请的权限范围.scopes("all")// 配置grant_type 表示授权类型。 使用密码模式.authorizedGrantTypes("password");}/*** 使用密码模式所需配置* AuthorizationServerEndpointsConfigurer 访问端点配置 是一个装载类* 装载Endpoints所有相关的类配置(AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService)。* @param endpoints*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.tokenStore(inMemoryTokenStore) //配置令牌的存储(这里存放在内存中).authenticationManager(authenticationManager).userDetailsService(userService);}
}
///
/// ///

/*** Description:* spring security配置* WebSecurityConfigurerAdapter是默认情况下 Spring security的http配置* 优先级高于ResourceServerConfigurer,用于保护oauth相关的endpoints,同时主要作用于用户的登录(form login,Basic auth)** @date:2020/10/29 9:41* @author: kaysanshi**/
@Configuration
@EnableWebSecurity
public class SecurityConfigure extends WebSecurityConfigurerAdapter {
    @Beanpublic PasswordEncoder passwordEncoder() {
    // 使用BCrypt强哈希函数加密方案(密钥迭代次数默认为10)return new BCryptPasswordEncoder();}/*** 为了让认证配置类注入使用** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();}/*** @param http* @throws Exception*/@Overrideprotected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable().authorizeRequests().antMatchers("/oauth/**", "/login/**", "logout/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll();}
}

将token存到redis

/*** Description:* 配置把token存到redis 中的redis链接* @date:2020/10/29 15:14* @author: kaysanshi**/
@Configuration
public class TokenStoreConfigure {
    @Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Beanpublic TokenStore redisTokenStore(){
    return new RedisTokenStore(redisConnectionFactory);}
}

在授权服务器中使用redis进行存储

package com.kaysanshi.springsecurityoauth2.configure;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.core.userdetails.UserDetailsService;
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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;/*** Description: 授权服务器的配置* author2配置* AuthorizationServerConfigurerAdapter 包括:* ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。* AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.* AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。** @date:2020/10/29 9:30* @author: kaysanshi**/
@Configuration
@EnableAuthorizationServer // 开启认证授权服务器
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
    // 该对象用来支持 password 模式@AutowiredAuthenticationManager authenticationManager;// 将令牌信息存储redis中@AutowiredTokenStore redisToken;// 该对象将为刷新token提供支持@AutowiredUserDetailsService userDetailsService;/*** 配置密码加密,因为再UserDetailsService是依赖与这个类的。** @return*/// 指定密码的加密方式@BeanPasswordEncoder passwordEncoder() {
    // 使用BCrypt强哈希函数加密方案(密钥迭代次数默认为10)return new BCryptPasswordEncoder();}/*** ClientDetailsServiceConfigurer* 主要是注入ClientDetailsService实例对象(唯一配置注入)。其它地方可以通过ClientDetailsServiceConfigurer调用开发配置的ClientDetailsService。* 系统提供的二个ClientDetailsService实现类:JdbcClientDetailsService、InMemoryClientDetailsService。** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients)throws Exception {
    // 配置一个客户端用于password认证的。同时也可以同时配置两个,可以再配置一个基于client认证的。clients.inMemory().withClient("password").authorizedGrantTypes("password", "refresh_token") //授权模式为password和refresh_token两种.accessTokenValiditySeconds(1800) // 配置access_token的过期时间.resourceIds("rid") //配置资源id.scopes("all") // 允许授权范围.secret("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") //123加密后的密码;}/*** AuthorizationServerEndpointsConfigurer 访问端点配置 是一个装载类* 装载Endpoints所有相关的类配置(AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService)。* tokenService用于存到redis中** @param endpoints*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.tokenStore(redisToken) //配置令牌的存到redis.authenticationManager(authenticationManager).userDetailsService(userDetailsService);}/*** AuthorizationServerSecurityConfigurer继承SecurityConfigurerAdapter.* 也就是一个 Spring Security安全配置提供给AuthorizationServer去配置AuthorizationServer的端点(/oauth/****)的安全访问规则、过滤器Filter。** @param security*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) {
    // 表示支持 client_id 和 client_secret 做登录认证// 允许使用表单认证security.allowFormAuthenticationForClients();}
}
  相关解决方案