轉載自:https://blog.csdn.net/qq_26977063/article/details/80474750
spring boot整合OAuth2保證api接口安全
1、 OAuth 概念
OAuth 是一個開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而不需要將用戶名和密碼提供給第三方應用。OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站在特定的時段內訪問特定的資源。這樣,OAuth讓用戶可以授權第三方網站訪問他們存儲在另外服務提供者的某些特定信息,而非所有內容。
2、OAuth 2.0 認證流程
第一步:得到授權碼 code
首先直接跳轉至用戶授權地址,即圖示 Request User Url ,提示用戶進行登錄,並給予相關資源授權,得到唯一的 Auth code ,這裏注意的是 code 只有 10 分鐘的有效期,對於安全考慮,相對於 OAuth 1.0 省了一步獲取臨時的 Token ,並且有效期也進行了控制,比 1.0 認證簡化了很多,並安全一些;
第二步:獲取 access token
得到授權 code 後,就是請求 access token ,通過圖示 Request access url ,生成得到數據 Token ;
第三步:通過 access token, 獲取 OpenID
通過 Access Token 請求 OpenID , OpenID 是用戶在此平臺的唯一標識,通過圖示 Request info url 請求,然後得到 OpenID ;
第四步:通過 access token 及 OpenID 調用 API,獲取用戶授權信息
通過第二步得到的數據 Token 、第三步得到的 OpenID 及相關 API ,進行請求,獲取用戶授權資源信息。
3、OAuth 授權模式
OAuth2.0 定義了 四種授權模式。分別爲:
授權碼模式
簡化模式
密碼模式
客戶端模式
4、oauth2 實例
可以分爲簡易的分爲三個步驟
配置資源服務器
配置認證服務器
配置spring security
4.1、構建工程
pom文件添加oauth2依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
4.2、配置資源服務器
@EnableResourceServer註解來開啓資源服務器
package com.vesus.springbootoauth2.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private Logger logger = LoggerFactory.getLogger(ResourceServerConfiguration.class);
@Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint ;
@Bean
public CustomLogoutSuccessHandler customLogoutSuccessHandler(){
return new CustomLogoutSuccessHandler();
} ;
@Override
public void configure(HttpSecurity http) throws Exception {
logger.info("=========================111111111=========");
http.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler())
.and()
.authorizeRequests()
.antMatchers("/hello/").permitAll()
.antMatchers("/secure/**").authenticated();
}
}
4.3、自定義401錯誤碼內容
package com.vesus.springbootoauth2.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Access Denied");
}
}
4.4、定義登出控制
退出系統時需要訪問SpringSecrutiy的logout方法來清空對應的token信息
package com.vesus.springbootoauth2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler {
private static final String BEARER_AUTHENTICATION = "Bearer ";
private static final String HEADER_AUTHORIZATION = "authorization";
@Autowired
private TokenStore tokenStore ;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String token = request.getHeader(HEADER_AUTHORIZATION);
if (token!=null&&token.startsWith(BEARER_AUTHENTICATION)){
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token.split(" ")[0]);
if (oAuth2AccessToken!=null){
tokenStore.removeAccessToken(oAuth2AccessToken);
}
}
response.setStatus(HttpServletResponse.SC_OK);
}
}
4.5、配置OAuth2驗證服務器
@EnableAuthorizationServer註解開啓驗證服務器
package com.vesus.springbootoauth2.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
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.JdbcTokenStore;
import javax.sql.DataSource;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver ;
@Autowired
private DataSource dataSource ;
@Bean
public TokenStore tokenStore(){
//這個是基於JDBC的實現,令牌(Access Token)會保存到數據庫
return new JdbcTokenStore(dataSource);
}
@Autowired
@Qualifier("authenticationManagerBean")//認證方式
private AuthenticationManager authenticationManager ;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager) ;
}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() // 使用in-memory存儲
.withClient(propertyResolver.getProperty(PROP_CLIENTID))//client_id用來標識客戶的Id
.scopes("read", "write") //允許授權範圍
.authorities("ROLE_ADMIN","ROLE_USER")//客戶端可以使用的權限
.authorizedGrantTypes("password", "refresh_token")//允許授權類型
.secret(propertyResolver.getProperty(PROP_SECRET))//secret客戶端安全碼
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
@Override
public void setEnvironment(Environment environment) {
//獲取到前綴是"authentication.oauth." 的屬性列表值.
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
4.6、安全配置
package com.vesus.springbootoauth2.config;
import com.vesus.springbootoauth2.service.impl.CustomUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService customUserService(){
return new CustomUserService();
}
//配置全局設置
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//設置UserDetailsService以及密碼規則
auth.userDetailsService(customUserService());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/hello") ;
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean() ;
}
//開啓全局方法攔截
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
4.7、啓動
訪問localhost:8080/hello
hello
使用postman訪問localhost:8080/oauth/token?username=admin&password=admin&grant_type=password
{
"access_token": "acf03e60-ed0a-4809-9ee0-240b81aab2d1",
"token_type": "bearer",
"refresh_token": "5b4a562e-704d-442a-9dfe-4aebad930e9d",
"expires_in": 1799,
"scope": "read write"
}
訪問:http://127.0.0.1:8080/login?access_token=b39c8a28-18fb-4d79-93e6-40f7203b8049
login
源碼:https://gitee.com/vesus198/springboot-demo/tree/master/springboot-oauth2
---------------------
作者:遊歷三界外不再五行中
來源:CSDN
原文:https://blog.csdn.net/qq_26977063/article/details/80474750
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!