Spring Security Oauth2
Posted 404 Not Found
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security Oauth2相关的知识,希望对你有一定的参考价值。
Spring Security Oauth2
授权服务器
-
Authorize Endpoint
:授权端点,进行授权 -
Token Endpoint
:令牌端点,经过授权拿到对应的Token -
Introspection Endpoint
:校验端点,校验Token的合法性 -
Revocation Endpoint
:撤销端点,撤销授权
Spring Security Oauth2架构
流程:
-
用户访问,此时没有Token。Oauth2RestTemplate会报错,这个报错信息会被Oauth2ClientContextFilter捕获并重定向到认证服务器
-
认证服务器通过Authorization Endpoint进行授权,并通过AuthorizationServerTokenServices生成授权码并返回给客户端
-
客户端拿到授权码去认证服务器通过Token Endpoint调用AuthorizationServerTokenServices生成Token并返回给客户端
-
客户端拿到Token去资源服务器访问资源,一般会通过Oauth2AuthenticationManager调用ResourceServerTokenServices进行校验。校验通过可以获取资源。
Spring Security Oauth2授权码模式
创建项目
2、添加依赖
<?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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yjxxt</groupId>
<artifactId>springsecurityoauth2demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsecurityoauth2demo</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>
</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、编写实体类
User.java
package com.yjxxt.springsecurityoauth2demo.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* 用户类
*/
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;
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
return authorities;
@Override
public String getPassword()
return password;
@Override
public String getUsername()
return username;
@Override
public boolean isAccountNonExpired()
return true;
@Override
public boolean isAccountNonLocked()
return true;
@Override
public boolean isCredentialsNonExpired()
return true;
@Override
public boolean isEnabled()
return true;
4、编写Service
UserService.java
package com.yjxxt.springsecurityoauth2demo.service;
import com.yjxxt.springsecurityoauth2demo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
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;
/**
* @author ylc
* @since 1.0.0
*/
@Service
public class UserService implements UserDetailsService
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
String password = passwordEncoder.encode("123456");
return new User("admin",password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
5、编写Controller
UserController.java
package com.yjxxt.springsecurityoauth2demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ylc
* @since 1.0.0
*/
@RestController
@RequestMapping("/user")
public class UserController
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication)
return authentication.getPrincipal();
6、编写配置类
SecurityConfig.java
package com.yjxxt.springsecurityoauth2demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring Security 配置类
*
* @author ylc
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Bean
public PasswordEncoder passwordEncoder()
return new BCryptPasswordEncoder();
@Override
public void configure(HttpSecurity http) throws Exception
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/oauth/**", "/login/**", "/logout/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
AuthorizationServerConfig.java
package com.yjxxt.springsecurityoauth2demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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;
/**
* 授权服务器配置
* @author ylc
* @since 1.0.0
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.inMemory()
//配置client_id
.withClient("admin")
//配置client-secret
.secret(passwordEncoder.encode("112233"))
//配置redirect_uri,用于授权成功后跳转
.redirectUris("http://www.baidu.com")
//配置申请的权限范围
.scopes("all")
//配置grant_type,表示授权类型
.authorizedGrantTypes("authorization_code");
ResourceServerConfig.java
package com.yjxxt.springsecurityoauth2demo.config;
import org.springframework.context.annotation.Configuration;
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;
/**
* 资源服务器配置
*
* @author ylc
* @since 1.0.0
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter
@Override
public void configure(HttpSecurity http) throws Exception
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/user/**");//配置需要保护的资源路径
7、测试
获取授权码
附带上客户端id、回调url、获取请求的类型
输入账户密码
点击授权获取授权码
根据授权码获取令牌(POST请求)
-
grant_type
:授权类型,填写authorization_code,表示授权码模式 -
code
:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。 -
client_id
:客户端标识 -
redirect_uri
:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。 -
scope
:授权范围。
认证失败服务端返回 401 Unauthorized
注意:此时无法请求到令牌,访问服务器会报错
根据token去资源服务器拿资源
如果修改token就会报错
Spring Security Oauth2 密码模式
在上面的代码中进行适当的修改即可
SecurityConfig.java
package com.yjxxt.springsecurityoauth2demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring Security 配置类
*
* @author ylc
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Bean
public PasswordEncoder passwordEncoder()
return new BCryptPasswordEncoder();
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
return super.authenticationManagerBean();
@Override
public void configure(HttpSecurity http) throws Exception
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/oauth/**", "/login/**", "/logout/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
AuthorizationServerConfig.java
package com.yjxxt.springsecurityoauth2demo.config;
import com.yjxxt.springsecurityoauth2demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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;
/**
* 授权服务器配置
* @author ylc
* @since 1.0.0
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserService userService;
/**
* 使用密码模式需要配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService);
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.inMemory()
//配置client_id
.withClient("admin")
//配置client-secret
.secret(passwordEncoder.encode("112233"))
//配置访问token的有效期
.accessTokenValiditySeconds(3600)
//配置刷新token的有效期
.refreshTokenValiditySeconds(864000)
//配置redirect_uri,用于授权成功后跳转
.redirectUris("http://www.baidu.com")
//配置申请的权限范围
.scopes("all")
//配置grant_type,表示授权类型
.authorizedGrantTypes("authorization_code","password");
测试:
在Redis中存储token
之前的代码我们将token直接存在内存中,这在生产环境中是不合理的,下面我们将其改造成存储在Redis中
添加依赖及配置
pom.xml
<!--redis 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- commons-pool2 对象池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
application.properties
# Redis配置
spring.redis.host=192.168.10.100
编写Redis配置类
RedisConfig.java
package com.yjxxt.springsecurityoauth2demo.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.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
/**
* 使用redis存储token的配置
* @author ylc
* @since 1.0.0
*/
@Configuration
public class RedisConfig
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore redisTokenStore()
return new RedisTokenStore(redisConnectionFactory);
在认证服务器配置中指定令牌的存储策略为Redis
package com.yjxxt.springsecurityoauth2demo.config;
import com.yjxxt.springsecurityoauth2demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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;
/**
* 授权服务器配置
* @author ylc
* @since 1.0.0
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserService userService;
@Autowired
@Qualifier("redisTokenStore")
private TokenStore tokenStore;
/**
* 使用密码模式需要配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userService)
.tokenStore(tokenStore);
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
clients.inMemory()
//配置client_id
.withClient("admin")
//配置client-secret
.secret(passwordEncoder.encode("112233"))
//配置访问token的有效期
.accessTokenValiditySeconds(3600)
//配置刷新token的有效期
.refreshTokenValiditySeconds(864000)
//配置redirect_uri,用于授权成功后跳转
.redirectUris("http://www.baidu.com")
//配置申请的权限范围
.scopes("all")
//配置grant_type,表示授权类型
.authorizedGrantTypes("password");
测试:
使用密码模式请求token
以上是关于Spring Security Oauth2的主要内容,如果未能解决你的问题,请参考以下文章
spring security oauth2认证中心 集成zuul网关的代码分析
Spring Security 入门(1-3)Spring Security oauth2.0 指南
Spring Security Oauth2 客户端获取访问令牌失败,请求代码无效=415,消息=不支持的媒体类型
spring security oAuth2.0 数据库说明