Springboot開發(fā)OAuth2認(rèn)證授權(quán)與資源服務(wù)器操作
設(shè)計(jì)并開發(fā)一個(gè)開放平臺(tái)。
一、設(shè)計(jì):
網(wǎng)關(guān)可以 與認(rèn)證授權(quán)服務(wù)合在一起,也可以分開。
二、開發(fā)與實(shí)現(xiàn):
用Oauth2技術(shù)對(duì)訪問受保護(hù)的資源的客戶端進(jìn)行認(rèn)證與授權(quán)。
Oauth2技術(shù)應(yīng)用的關(guān)鍵是:
1)服務(wù)器對(duì)OAuth2客戶端進(jìn)行認(rèn)證與授權(quán)。
2)Token的發(fā)放。
3)通過access_token訪問受OAuth2保護(hù)的資源。
選用的關(guān)鍵技術(shù):Springboot, Spring-security, Spring-security-oauth2。
提供一個(gè)簡化版,用戶、token數(shù)據(jù)保存在內(nèi)存中,用戶與客戶端的認(rèn)證授權(quán)服務(wù)、資源服務(wù),都是在同一個(gè)工程中?,F(xiàn)實(shí)項(xiàng)目中,技術(shù)架構(gòu)通常上將用戶與客戶端的認(rèn)證授權(quán)服務(wù)設(shè)計(jì)在一個(gè)子系統(tǒng)(工程)中,而資源服務(wù)設(shè)計(jì)為另一個(gè)子系統(tǒng)(工程)。
1、Spring-security對(duì)用戶身份進(jìn)行認(rèn)證授權(quán):
主要作用是對(duì)用戶身份通過用戶名與密碼的方式進(jìn)行認(rèn)證并且授權(quán)。
package com.banling.oauth2server.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.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception { //用戶信息保存在內(nèi)存中 //在鑒定角色roler時(shí),會(huì)默認(rèn)加上ROLLER_前綴 auth.inMemoryAuthentication().withUser("user").password("user").roles("USER").and() .withUser("test").password("test").roles("TEST"); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() //登記界面,默認(rèn)是permit All .and() .authorizeRequests().antMatchers("/","/home").permitAll() //不用身份認(rèn)證可以訪問 .and() .authorizeRequests().anyRequest().authenticated() //其它的請(qǐng)求要求必須有身份認(rèn)證 .and() .csrf() //防止CSRF(跨站請(qǐng)求偽造)配置 .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
配置用戶信息,保存在內(nèi)存中。也可以自定義將用戶數(shù)據(jù)保存在數(shù)據(jù)庫中,實(shí)現(xiàn)UserDetailsService接口,進(jìn)行認(rèn)證與授權(quán),略。
配置訪問哪些URL需要授權(quán)。必須配置authorizeRequests(),否則啟動(dòng)報(bào)錯(cuò),說是沒有啟用security技術(shù)。
注意,在這里的身份進(jìn)行認(rèn)證與授權(quán)沒有涉及到OAuth的技術(shù):
當(dāng)訪問要授權(quán)的URL時(shí),請(qǐng)求會(huì)被DelegatingFilterProxy攔截,如果還沒有授權(quán),請(qǐng)求就會(huì)被重定向到登錄界面。在登錄成功(身份認(rèn)證并授權(quán))后,請(qǐng)求被重定向至之前訪問的URL。
2、OAuth2的授權(quán)服務(wù):
主要作用是OAuth2的客戶端進(jìn)行認(rèn)證與授權(quán)。
package com.banling.oauth2server.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.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.approval.ApprovalStore; import org.springframework.security.oauth2.provider.approval.TokenApprovalStore; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; @Configuration @EnableAuthorizationServer public class AuthServerConfig extends AuthorizationServerConfigurerAdapter{ @Autowired private TokenStore tokenStore; @Autowired private AuthenticationManager authenticationManager; @Autowired private ApprovalStore approvalStore; @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //添加客戶端信息 //使用內(nèi)存存儲(chǔ)OAuth客戶端信息 clients.inMemory() // client_id .withClient("client") // client_secret .secret("secret") // 該client允許的授權(quán)類型,不同的類型,則獲得token的方式不一樣。 .authorizedGrantTypes("authorization_code","implicit","refresh_token") .resourceIds("resourceId") //回調(diào)uri,在authorization_code與implicit授權(quán)方式時(shí),用以接收服務(wù)器的返回信息 .redirectUris("http://localhost:8090/") // 允許的授權(quán)范圍 .scopes("app","test"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //reuseRefreshTokens設(shè)置為false時(shí),每次通過refresh_token獲得access_token時(shí),也會(huì)刷新refresh_token;也就是說,會(huì)返回全新的access_token與refresh_token。 //默認(rèn)值是true,只返回新的access_token,refresh_token不變。 endpoints.tokenStore(tokenStore).approvalStore(approvalStore).reuseRefreshTokens(false) .authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.realm("OAuth2-Sample") .allowFormAuthenticationForClients() .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()"); } @Bean public TokenStore tokenStore() { //token保存在內(nèi)存中(也可以保存在數(shù)據(jù)庫、Redis中)。 //如果保存在中間件(數(shù)據(jù)庫、Redis),那么資源服務(wù)器與認(rèn)證服務(wù)器可以不在同一個(gè)工程中。 //注意:如果不保存access_token,則沒法通過access_token取得用戶信息 return new InMemoryTokenStore(); } @Bean public ApprovalStore approvalStore() throws Exception { TokenApprovalStore store = new TokenApprovalStore(); store.setTokenStore(tokenStore); return store; } }
配置OAuth2的客戶端信息:clientId、client_secret、authorization_type、redirect_url等。本例是將數(shù)據(jù)保存在內(nèi)存中。也可以保存在數(shù)據(jù)庫中,實(shí)現(xiàn)ClientDetailsService接口,進(jìn)行認(rèn)證與授權(quán),略。
TokenStore是access_token的存儲(chǔ)單元,可以保存在內(nèi)存、數(shù)據(jù)庫、Redis中。本例是保存在內(nèi)存中。
3、OAuth2的資源服務(wù):
主要作用是配置資源受保護(hù)的OAuth2策略。
package com.banling.oauth2server.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; 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.token.TokenStore; @Configuration @EnableResourceServer public class ResServerConfig extends ResourceServerConfigurerAdapter{ @Autowired private TokenStore tokenStore; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources .tokenStore(tokenStore) .resourceId("resourceId"); } @Override public void configure(HttpSecurity http) throws Exception { /* 注意: 1、必須先加上: .requestMatchers().antMatchers(...),表示對(duì)資源進(jìn)行保護(hù),也就是說,在訪問前要進(jìn)行OAuth認(rèn)證。 2、接著:訪問受保護(hù)的資源時(shí),要具有哪里權(quán)限。 ------------------------------------ 否則,請(qǐng)求只是被Security的攔截器攔截,請(qǐng)求根本到不了OAuth2的攔截器。 同時(shí),還要注意先配置:security.oauth2.resource.filter-order=3,否則通過access_token取不到用戶信息。 ------------------------------------ requestMatchers()部分說明: Invoking requestMatchers() will not override previous invocations of :: mvcMatcher(String)}, requestMatchers(), antMatcher(String), regexMatcher(String), and requestMatcher(RequestMatcher). */ http // Since we want the protected resources to be accessible in the UI as well we need // session creation to be allowed (it's disabled by default in 2.0.6) //另外,如果不設(shè)置,那么在通過瀏覽器訪問被保護(hù)的任何資源時(shí),每次是不同的SessionID,并且將每次請(qǐng)求的歷史都記錄在OAuth2Authentication的details的中 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .requestMatchers() .antMatchers("/user","/res/**") .and() .authorizeRequests() .antMatchers("/user","/res/**") .authenticated(); } }
配置哪些URL資源是受OAuth2保護(hù)的。注意,必須配置sessionManagement(),否則訪問受護(hù)資源請(qǐng)求不會(huì)被OAuth2的攔截器ClientCredentialsTokenEndpointFilter與OAuth2AuthenticationProcessingFilter攔截,也就是說,沒有配置的話,資源沒有受到OAuth2的保護(hù)。
4、受OAuth2保存的資源:
1)獲取OAuth2客戶端的信息
package com.banling.oauth2server.web; import java.security.Principal; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @RequestMapping("/user") public Principal user(Principal principal) { //principal在經(jīng)過security攔截后,是org.springframework.security.authentication.UsernamePasswordAuthenticationToken //在經(jīng)OAuth2攔截后,是OAuth2Authentication return principal; } }
2)其它受保護(hù)的資源
package com.banling.oauth2server.web; import java.security.Principal; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 作為OAuth2的資源服務(wù)時(shí),不能在Controller(或者RestController)注解上寫上URL,因?yàn)檫@樣不會(huì)被識(shí)別,會(huì)報(bào)404錯(cuò)誤。<br> *<br> { *<br> "timestamp": 1544580859138, *<br> "status": 404, *<br> "error": "Not Found", *<br> "message": "No message available", *<br> "path": "/res/getMsg" *<br> } *<br> * * */ @RestController()//作為資源服務(wù)時(shí),不能帶上url,@RestController("/res")是錯(cuò)的,無法識(shí)別。只能在方法上注解全路徑 public class ResController { @RequestMapping("/res/getMsg") public String getMsg(String msg,Principal principal) {//principal中封裝了客戶端(用戶,也就是clientDetails,區(qū)別于Security的UserDetails,其實(shí)clientDetails中也封裝了UserDetails),不是必須的參數(shù),除非你想得到用戶信息,才加上principal。 return "Get the msg: "+msg; } }
5、application.properties配置:
security.oauth2.resource.filter-order=3 必須配置,否則對(duì)受護(hù)資源請(qǐng)求不會(huì)被OAuth2的攔截器攔截。
6、測(cè)試
1)authorization_code方式獲取code,然后再通過code獲取access_token(和refresh_token)。
在瀏覽輸入:
http://localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://localhost:8090/
在登錄界面輸入用戶名與密碼user/user,提交。
提交后服務(wù)重定向 至scope的授權(quán)界面:
授權(quán)后,在回調(diào)uri中可以得從code:
用postman工具,設(shè)置header的值,通過code獲取access_token與fresh_token:
2)implict方式接獲取access_token。
瀏覽器中輸入:
http://localhost:8080/oauth/authorize?client_id=client&response_type=token&redirect_uri=http://localhost:8090/
可以直接獲得access_token。
3)通過refresh_token獲取access_token與refresh_token。
用postman工具測(cè)試,根據(jù)refresh_token獲取新的access_token與fresh_token。
4)獲取OAuth2客戶端的信息。
可以通過get方式,也可以通過設(shè)置header獲取。
get方式,看url字符串:
設(shè)置header的方式:
5)訪問其它受保護(hù)的資源
github上的源碼: https://github.com/banat020/OAuth2-server
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- springboot oauth2實(shí)現(xiàn)單點(diǎn)登錄實(shí)例
- springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程
- 基于SpringBoot整合oauth2實(shí)現(xiàn)token認(rèn)證
- springboot2.x實(shí)現(xiàn)oauth2授權(quán)碼登陸的方法
- 詳解Springboot Oauth2 Server搭建Oauth2認(rèn)證服務(wù)
- 使用Springboot搭建OAuth2.0 Server的方法示例
- springboot+Oauth2實(shí)現(xiàn)自定義AuthenticationManager和認(rèn)證path
- SpringBoot3.X配置OAuth的代碼實(shí)踐
相關(guān)文章
二種jar包制作方法講解(dos打包jar eclipse打包jar文件)
這篇文章主要介紹了二種jar包制作方法講解:dos打包jar和eclipse打包jar文件,大家參考使用吧2013-11-11Java生產(chǎn)者消費(fèi)者模式實(shí)例分析
這篇文章主要介紹了Java生產(chǎn)者消費(fèi)者模式,結(jié)合實(shí)例形式分析了java生產(chǎn)者消費(fèi)者模式的相關(guān)組成、原理及實(shí)現(xiàn)方法,需要的朋友可以參考下2019-03-03Spring中的攔截器HandlerInterceptor詳細(xì)解析
這篇文章主要介紹了Spring中的攔截器HandlerInterceptor詳細(xì)解析,HandlerInterceptor 是 Spring 框架提供的一個(gè)攔截器接口,用于在請(qǐng)求處理過程中攔截和處理請(qǐng)求,需要的朋友可以參考下2024-01-01Java中常見延時(shí)隊(duì)列的實(shí)現(xiàn)方案小結(jié)(建議收藏)
延時(shí)隊(duì)列它要具有隊(duì)列的特性,再給它附加一個(gè)延遲消費(fèi)隊(duì)列消息的功能,也就是說可以指定隊(duì)列中的消息在哪個(gè)時(shí)間點(diǎn)被消費(fèi),這篇文章主要介紹了Java中常見延時(shí)隊(duì)列的實(shí)現(xiàn)方案總結(jié),需要的朋友可以參考下2024-04-04Java讀取properties配置文件時(shí),出現(xiàn)中文亂碼的解決方法
下面小編就為大家?guī)硪黄狫ava讀取properties配置文件時(shí),出現(xiàn)中文亂碼的解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-11-11基于@RequestParam name和value屬性的區(qū)別
這篇文章主要介紹了@RequestParam name和value屬性的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Mybatis-Plus實(shí)現(xiàn)公共字段自動(dòng)賦值的方法
這篇文章主要介紹了Mybatis-Plus實(shí)現(xiàn)公共字段自動(dòng)賦值的方法,涉及到通用字段自動(dòng)填充的最佳實(shí)踐總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07