javascript
Spring Security + OAuth2.0
授權服務器
 授權服務器中有4個端點。說明如下:
- Authorize Endpoint :授權端點,進行授權。
 - Token Endpoint :令牌端點,經過授權拿到對應的Token。
 - lntrospection Endpoint :校驗端點,校驗Token的合法性。
 - Revocation Endpoint :撤銷端點,撤銷授權。
 
Spring Security Oauth2架構
 說明如下:
- 用戶訪問,此時沒有Token。Oauth2RestTemplate會報錯,這個報錯信息會被Oauth2ClientContextFilter捕獲并重定向到認證服務器。
 - 認證服務器通過Authorization Endpoint進行授權,并通過AuthorizationServerTokenServices生成授權碼并返回給客戶端。
 - 客戶端拿到授權碼去認證服務器通過Token Endpoint調用AuthorizationServerTokenServices生成Token并返回給客戶端。
 - 客戶端拿到Token去資源服務器訪問資源,一般會通過Oauth2AuthenticationManager調用ResourceServerTokenServices進行校驗。校驗通過可以獲取資源。
 
Spring Security Oauth2授權碼模式
環境搭建
 
 (2)pom依賴
(3)pojo
 在pojo包下自定一個實體類User,但是此類必實現UserDetails接口。如下:
(4)Spring Security配置類
config包下創建SecurityConfig配置類,如下:
package com.sec.kun.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;/*** @Author zhoukun* @Date 2021/2/17 15:55*/ /** Spring Security配置類*/ @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/oauth/**","/login/**","/logout/**").permitAll()//放行.anyRequest().authenticated()//其他路徑攔截.and().formLogin().permitAll()//表單提交放行.and().csrf().disable();//csrf關閉}// 注冊PasswordEncoder@Beanpublic PasswordEncoder getPasswordEncoder() {return new BCryptPasswordEncoder();} }(5)自定義登錄邏輯
service包下創建UserDetailsServiceImpl類,如下:
package com.sec.kun.service.Impl;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; import com.sec.kun.pojo.User; 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 zhoukun* @Date 2021/2/17 15:56*/ @Service public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//實際是根據用戶名去數據庫查,這里就直接用靜態數據了if(!username.equals("86547462")) {throw new UsernameNotFoundException("用戶名不存在!");}// 密碼加密String password = passwordEncoder.encode("123456");//創建User用戶,自定義的UserUser user = new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));return user;} }(6)認證服務配置
在config包下創建認證服務的配置類AuthorizationServerConfig,如下:
package com.sec.kun.config;/*** @Author zhoukun* @Date 2021/2/17 15:59*/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;/*** 授權服務器*/ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate PasswordEncoder passwordEncoder;@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()//內存中.withClient("client")//客戶端ID.secret(passwordEncoder.encode("zk2000208"))//秘鑰.redirectUris("https://www.bilibili.com")//重定向到的地址.scopes("all")//授權范圍.authorizedGrantTypes("authorization_code");//授權類型為授權碼模式} }(7)資源服務配置
在config包下創建資源服務的配置類
/** 資源服務配置*/ @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter{@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers("/user/**");}}(8)controller
在controller包下創建UserController類,如下:
@RestController @RequestMapping("/user") public class UserController {@RequestMapping("/getCurrentUser")public Object getCurrentUser(Authentication authentication) {return authentication.getPrincipal();}}測試
(1)獲取授權碼
啟動項目,訪問:http://localhost:8000/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
 說明:
 http://localhost:8000:這是項目端口。
 /oauth/authorize?response_type=code:獲取授權碼的固定寫法。
 client_id:這是客戶端ID,就是在授權服務中定義的:
 
 
 
 重定向到了百度首頁,并且拿到了授權碼。
 (2)獲取令牌
因為要發送post請求,所以使用postman。
url:http://localhost:8000/oauth/token
 
 左邊的type選擇Basic Auth,右邊的用戶名為客戶端ID,密碼是定義好的。
 發送請求,返回如下:
 
(3)獲取資源服務器資源
需要攜帶通行令牌來獲取。還是post請求:http://localhost:8000/user/getCurrentUser
 
Spring Security Oauth2密碼模式
環境搭建
直接在授權碼模式的基礎上進行修改了。
(1)修改SecurityConfig
直接在里面加:
//注冊AuthenticationManager@Beanpublic AuthenticationManager getAuthenticationManager() throws Exception {return super.authenticationManager();}(2)修改AuthorizationServerConfig
直接在里面加:
/*** 密碼模式*/@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsServiceImpl userDetailsServiceImpl;//密碼模式需要配置@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsServiceImpl);}然后下面添加密碼模式:
測試
(1)獲取Token令牌
啟動項目,直接在postman中發送:http://localhost:8000/oauth/token
 
 
 (2)獲取資源
現在直接可以攜帶令牌去訪問資源:http://localhost:8000/user/getCurrentUser
 
Redis中存儲Token令牌
將token直接存在內存中,這在生產環境中是不合理的,下面將其改造成存儲在Redis中。
(1)pom依賴
在pom中添加如下依賴:
<!-- redis依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>(2)yml配置
在application.yml中添加:
spring:redis:host: localhostport: 6379password: 123456在AuthorizationServerConfig加入
/*** redis工廠,默認使用lettue*/@Autowiredpublic RedisConnectionFactory redisConnectionFactory;修改
 
 (5)測試
 啟動工程,使用密碼模式獲取令牌:
 
查看redis:
SpringSecurity + OAuth2.0 + JWT
前面只使用Oauth2.0的話,頒發的通行令牌長度太短了,現在想整合JWT,將頒發的token轉換一下,轉換成jwt格式的長令牌。
JWT:JSON Web Token(JWT)是一個開放的行業標準(RFC 7519),它定義了一種簡介的、自包含的協議格式,用于在通信雙方傳遞json對象,傳遞的信息經過數字簽名可以被驗證和信任。JWT可以使用HMAC算法或使用RSA的公鑰/私鑰對來簽名,防止被篡改。
整合JWT
 直接在此工程的基礎上修改了。
 
pom
 
 (2)Redis配置類
注釋掉Redis的配置類。
 (3)Jwt配置類
在config包下創建JwtTokenStoreConfig配置類,如下:
package com.sec.kun.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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;/*** @Author zhoukun* @Date 2021/2/17 19:50*/ @Configuration public class JwtTokenStoreConfig {//注冊JwtAccessTokenConverter@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();//配置jwt秘鑰jwtAccessTokenConverter.setSigningKey("zhoukun");return jwtAccessTokenConverter;}//注冊TokenStore@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());} }(4)修改授權配置類
修改AuthorizationServerConfig類,如下:
/***Jwt配置類*/@AutowiredJwtAccessTokenConverter jwtAccessTokenConverter;@AutowiredTokenStore tokenStore;@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints)throws Exception {endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsServiceImpl).tokenStore(tokenStore).accessTokenConverter(jwtAccessTokenConverter);}
 (5)測試
 使用密碼模式獲取jwt令牌,如下:
 
 現在的access_token令牌的長度發生了變化,與它對應的是jti值。解析這個token值:
 
擴展JWT的內容
現在想往JWT令牌中添加自定義的內容,過程如下。
(1)Jwt內容增強器
創建一個jwt包,包下創建一個Jwt的內容增強器JwtTokenEnhancer,如下:
package com.sec.kun.hancer;import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.stereotype.Component;import java.util.HashMap; import java.util.Map;/*** @Author zhoukun* @Date 2021/2/17 19:56*/ @Component public class JwtTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication oAuth2Authentication) {//自定義的內容存到map中Map<String,Object> map = new HashMap<>();map.put("city","西安");map.put("like","yu");map.put("age",20);map.put("name","泡泡茶壺");//下轉型if(accessToken instanceof DefaultOAuth2AccessToken) {DefaultOAuth2AccessToken defaultOAuth2AccessToken = (DefaultOAuth2AccessToken)accessToken;defaultOAuth2AccessToken.setAdditionalInformation(map);return defaultOAuth2AccessToken;}return null;} }(2)修改Jwt配置類
修改Jwt配置類JwtTokenStoreConfig,如下:
 
(4)測試
 
 解析生成的jwt令牌:
 
解析JWT的內容
JWT令牌的內容一般要在java程序中解析出來,以下演示過程。
(1)pom依賴
還是使用jjwt來解析。pom中添加依賴:
<!-- jjwt依賴 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>(2)controller
 修改UserController,如下:
測試
 獲取令牌
帶著令牌獲取資源
 
 獲取到了,返回的是jwt令牌解析后的內容。
JWT刷新令牌
在Spring Cloud Security中使用oauth2時,如果令牌失效了,可以使用刷新令牌通過refresh_token的授權模式再次獲取access_token,只需修改認證服務器的配置,添加refresh_token的授權模式即可。
修改授權服務配置類AuthorizationServerConfig,如下:
 
 測試:1分鐘后再發請求:
 
 獲取不到資源了,現在jwt通行令牌已經過期了。解決方法是加一個刷新令牌refresh_token。如下:
 
 然后再啟動工程獲取令牌:
 
 等1分鐘,用通行令牌獲取資源:
 
 通行令牌過期了。現在使用刷新令牌直接從授權服務端獲取新的通行令牌:
 
 
 
 獲取到了新的通行令牌和刷新令牌。同樣的,通行令牌的有效期還是1分鐘,刷新令牌是1小時,再用這個新的通行令牌獲取資源:
 
 資源獲取成功。
總結
以上是生活随笔為你收集整理的Spring Security + OAuth2.0的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Codeforces 786ABerze
 - 下一篇: 手机绑定sim卡