文章插圖

文章插圖
背景分析
傳統的登錄系統中 , 每個站點都實現了自己的專用登錄模塊 。各站點的登錄狀態相互不認可 , 各站點需要逐一手工登錄 。例如:
單點登陸系統
單點登錄 , 英文是 Single Sign On(縮寫為 SSO) 。即多個站點共用一臺認證授權服務器 , 用戶在其中任何一個站點登錄后 , 可以免登錄訪問其他所有站點 。而且 , 各站點間可以通過該登錄狀態直接交互 。例如:
基于資源服務工程添加單點登陸認證和授權服務 , 工程結構定義如下:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency>構建項目配置文件在sca-auth工程中創建bootstrap.yml文件 , 例如:
server:port: 8071spring:application:name: sca-authcloud:nacos:discovery:server-addr: localhost:8848config:server-addr: localhost:8848添加項目啟動類package com.jt;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ResourceAuthApplication {public static void main(String[] args) {SpringApplication.run(ResourceAuthApplication.class, args);}}啟動并訪問項目項目啟動時 , 系統會默認生成一個登陸密碼 , 例如:
我們的單點登錄系統最終會按照如下結構進行設計和實現,例如:
定義安全配置類
修改SecurityConfig配置類,添加登錄成功或失敗的處理邏輯,例如:
package com.jt.auth.config;import com.fasterxml.jackson.databind.ObjectMapper;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.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.web.authentication.AuthenticationFailureHandler;import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {/**初始化密碼加密對象*/@Beanpublic BCryptPasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}/**在這個方法中定義登錄規則* 1)對所有請求放行(當前工程只做認證)* 2)登錄成功信息的返回* 3)登錄失敗信息的返回* */@Overrideprotected void configure(HttpSecurity http) throws Exception {//關閉跨域工具http.csrf().disable();//放行所有請求http.authorizeRequests().anyRequest().permitAll();//登錄成功與失敗的處理http.formLogin().successHandler(successHandler()).failureHandler(failureHandler());}@Beanpublic AuthenticationSuccessHandler successHandler(){//return new AuthenticationSuccessHandler() {//@Override//public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {////}//}return (request,response,authentication) ->{//1.構建map對象,封裝響應數據Map<String,Object> map=new HashMap<>();map.put("state",200);map.put("message","login ok");//2.將map對象寫到客戶端writeJsonToClient(response,map);};}@Beanpublic AuthenticationFailureHandler failureHandler(){return (request,response, e)-> {//1.構建map對象,封裝響應數據Map<String,Object> map=new HashMap<>();map.put("state",500);map.put("message","login failure");//2.將map對象寫到客戶端writeJsonToClient(response,map);};}private void writeJsonToClient(HttpServletResponse response,Object object) throws IOException {//1.將對象轉換為json//將對象轉換為json有3種方案://1)Google的Gson-->toJson(需要自己找依賴)//2)阿里的fastjson-->JSON (spring-cloud-starter-alibaba-sentinel)//3)Springboot web自帶的jackson-->writeValueAsString (spring-boot-starter-web)//我們這里借助springboot工程中自帶的jackson//jackson中有一個對象類型為ObjectMapper,它內部提供了將對象轉換為json的方法//例如:String jsonStr=new ObjectMapper().writeValueAsString(object);//3.將json字符串寫到客戶端PrintWriter writer = response.getWriter();writer.println(jsonStr);writer.flush();}}定義用戶信息處理對象在spring security應用中底層會借助UserDetailService對象獲取數據庫信息,并進行封裝,最后返回給認證管理器,完成認證操作,例如:
package com.jt.auth.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.userdetails.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.bcrypt.BCryptPasswordEncoder;import org.springframework.stereotype.Service;import java.util.List;/** * 登錄時用戶信息的獲取和封裝會在此對象進行實現, * 在頁面上點擊登錄按鈕時,會調用這個對象的loadUserByUsername方法, * 頁面上輸入的用戶名會傳給這個方法的參數 [email protected] class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate BCryptPasswordEncoder passwordEncoder;//UserDetails用戶封裝用戶信息(認證和權限信息)@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {//1.基于用戶名查詢用戶信息(用戶名,用戶狀態,密碼,....)//Userinfo userinfo=userMapper.selectUserByUsername(username);String encodedPassword=passwordEncoder.encode("123456");//2.查詢用戶權限信息(后面會訪問數據庫)//這里先給幾個假數據List<GrantedAuthority> authorities =AuthorityUtils.createAuthorityList(//這里的權限信息先這么寫,后面講"sys:res:create", "sys:res:retrieve");//3.對用戶信息進行封裝return new User(username,encodedPassword,authorities);}}網關中登陸路由配置在網關配置文件中添加登錄路由配置,例如
- id: router02uri: lb://sca-auth#lb表示負載均衡,底層默認使用ribbon實現predicates: #定義請求規則(請求需要按照此規則設計)- Path=/auth/login/** #請求路徑設計filters:- StripPrefix=1 #轉發之前去掉path中第一層路徑基于Postman進行訪問測試啟動sca-gateway , sca-auth服務 , 然后基于postman訪問網關,執行登錄測試 , 例如:
在sca-resource-ui工程的static目錄中定義登陸頁面 , 例如:
<!doctype html><html lang="en"><head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><!-- Bootstrap CSS --><link rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"><title>login</title></head><body><div class="container"id="app"><h3>Please Login</h3><form><div class="mb-3"><label for="usernameId" class="form-label">Username</label><input type="text" v-model="username" class="form-control" id="usernameId" aria-describedby="emailHelp"></div><div class="mb-3"><label for="passwordId" class="form-label">Password</label><input type="password" v-model="password" class="form-control" id="passwordId"></div><button type="button" @click="doLogin()" class="btn btn-primary">Submit</button></form></div><script src="http://www.mnbkw.com/jxjc/188274/https:[email?protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script><script src="http://img.hubeilong.com/220624/2331521015-2.jpg"></script><script src="http://img.hubeilong.com/220624/233152L15-3.jpg"></script><script>var vm=new Vue({el:"#app",//定義監控點 , vue底層會基于此監控點在內存中構建dom樹data:{ //此對象中定義頁面上要操作的數據username:"",password:""},methods: {//此位置定義所有業務事件處理函數doLogin() {//1.定義urllet url = "http://localhost:9000/auth/login"http://2.定義參數let params = new URLSearchParams()params.append('username',this.username);params.append('password',this.password);//3.發送異步請求axios.post(url, params).then((response) => {debuggerlet result=response.data;console.log(result);if (result.state == 200) {alert("login ok");} else {alert(result.message);}})}}});</script></body></html>啟動sca-resource-ui服務后 , 進入登陸頁面 , 輸入用戶名jack,密碼123456進行登陸測試 。頒發登陸成功令牌構建令牌配置對象
本次我們借助JWT(Json Web Token-是一種json格式)方式將用戶相關信息進行組織和加密,并作為響應令牌(Token),從服務端響應到客戶端,客戶端接收到這個JWT令牌之后,將其保存在客戶端(例如localStorage),然后攜帶令牌訪問資源服務器,資源服務器獲取并解析令牌的合法性,基于解析結果判定是否允許用戶訪問資源.
package com.jt.auth.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;@Configurationpublic class TokenConfig {//定義簽名key,在執行令牌簽名需要這個key,可以自己指定.private String SIGNING_KEY = "auth";//定義令牌生成策略.@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}//定義Jwt轉換器,負責生成jwt令牌,解析令牌內容@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter converter=new JwtAccessTokenConverter();//設置加密/解密口令converter.setSigningKey(SIGNING_KEY);return converter;}}定義認證授權核心配置第一步:在SecurityConfig中添加如下方法(創建認證管理器對象 , 后面授權服務器會用到):
@Beanpublic AuthenticationManager authenticationManagerBean()throws Exception {return super.authenticationManagerBean();}第二步:所有零件準備好了開始拼裝最后的主體部分 , 這個主體部分就是授權服務器的核心配置package com.jt.auth.config;import lombok.AllArgsConstructor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpMethod;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.core.userdetails.UserDetailsService;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.AuthorizationServerTokenServices;import org.springframework.security.oauth2.provider.token.DefaultTokenServices;import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;import java.util.Arrays;/** * 完成所有配置的組裝,在這個配置類中完成認證授權,JWT令牌簽發等配置操作 * 1)SpringSecurity (安全認證和授權) * 2)TokenConfig * 3)Oauth2(暫時不說) [email protected]@[email protected] //開啟認證和授權服務public class Oauth2Config extends AuthorizationServerConfigurerAdapter {//此對象負責完成認證管理private AuthenticationManager authenticationManager;//TokenStore負責完成令牌創建,信息讀取private TokenStore tokenStore;//JWT令牌轉換器(基于用戶信息構建令牌,解析令牌)private JwtAccessTokenConverter jwtAccessTokenConverter;//密碼加密匹配器對象private PasswordEncoder passwordEncoder;//負責獲取用戶信息信息private UserDetailsService userDetailsService;//設置認證端點的配置(/oauth/token),客戶端通過這個路徑獲取JWT令牌@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints//配置認證管理器.authenticationManager(authenticationManager)//驗證用戶的方法獲得用戶詳情.userDetailsService(userDetailsService)//要求提交認證使用post請求方式,提高安全性.allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET)//要配置令牌的生成,由于令牌生成比較復雜,下面有方法實現.tokenServices(tokenService());//這個不配置,默認令牌為UUID.randomUUID().toString()}//定義令牌生成策略@Beanpublic AuthorizationServerTokenServices tokenService(){//這個方法的目標就是獲得一個令牌生成器DefaultTokenServices services=new DefaultTokenServices();//支持令牌刷新策略(令牌有過期時間)services.setSupportRefreshToken(true);//設置令牌生成策略(tokenStore在TokenConfig配置了,本次我們應用JWT-定義了一種令牌格式)services.setTokenStore(tokenStore);//設置令牌增強(允許設置令牌生成策略,默認是非jwt方式,現在設置為jwt方式,并在令牌Payload部分允許添加擴展數據,例如用戶權限信息)TokenEnhancerChain chain=new TokenEnhancerChain();chain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));services.setTokenEnhancer(chain);//設置令牌有效期services.setAccessTokenValiditySeconds(3600);//1小時//刷新令牌應用場景:一般在用戶登錄系統后 , 令牌快過期時 , 系統自動幫助用戶刷新令牌 , 提高用戶的體驗感services.setRefreshTokenValiditySeconds(3600*72);//3天return services;}//設置客戶端詳情類似于用戶詳情@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()//客戶端id (客戶端訪問時需要這個id).withClient("gateway-client")//客戶端秘鑰(客戶端訪問時需要攜帶這個密鑰).secret(passwordEncoder.encode("123456"))//設置權限.scopes("all")//all只是個名字而已和寫abc效果相同//允許客戶端進行的操作這里的認證方式表示密碼方式,里面的字符串千萬不能寫錯.authorizedGrantTypes("password","refresh_token");}// 認證成功后的安全約束配置,對指定資源的訪問放行,我們登錄時需要訪問/oauth/token,需要對這樣的url進行放行@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {//認證通過后,允許客戶端進行哪些操作security//公開oauth/token_key端點.tokenKeyAccess("permitAll()")//公開oauth/check_token端點.checkTokenAccess("permitAll()")//允許提交請求進行認證(申請令牌).allowFormAuthenticationForClients();}}配置網關認證的URL- id: router02uri: lb://sca-authpredicates:#- Path=/auth/login/**#沒要令牌之前,以前是這樣配置- Path=/auth/oauth/**#微服務架構下,需要令牌,現在要這樣配置filters:- StripPrefix=1Postman訪問測試第一步:啟動服務依次啟動sca-auth服務 , sca-resource-gateway服務 。
第二步:檢測sca-auth服務控制臺的Endpoints信息 , 例如:
登陸成功會在控制臺顯示令牌信息 , 例如:{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Mjk5OTg0NjAsInVzZXJfbmFtZSI6ImphY2siLCJhdXRob3JpdGllcyI6WyJzeXM6cmVzOmNyZWF0ZSIsInN5czpyZXM6cmV0cmlldmUiXSwianRpIjoiYWQ3ZDk1ODYtMjUwYS00M2M4LWI0ODYtNjIyYjJmY2UzMDNiIiwiY2xpZW50X2lkIjoiZ2F0ZXdheS1jbGllbnQiLCJzY29wZSI6WyJhbGwiXX0.-Zcmxwh0pz3GTKdktpr4FknFB1v23w-E501y7TZmLg4","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqYWNrIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImFkN2Q5NTg2LTI1MGEtNDNjOC1iNDg2LTYyMmIyZmNlMzAzYiIsImV4cCI6MTYzMDI1NDA2MCwiYXV0aG9yaXRpZXMiOlsic3lzOnJlczpjcmVhdGUiLCJzeXM6cmVzOnJldHJpZXZlIl0sImp0aSI6IjIyOTdjMTg2LWM4MDktNDZiZi1iNmMxLWFiYWExY2ExZjQ1ZiIsImNsaWVudF9pZCI6ImdhdGV3YXktY2xpZW50In0.1Bf5IazROtFFJu31Qv3rWAVEtFC1NHWU1z_DsgcnSX0","expires_in": 3599,"scope": "all","jti": "ad7d9586-250a-43c8-b486-622b2fce303b"}登陸頁面登陸方法設計登陸成功以后 , 將token存儲到localStorage中,修改登錄頁面的doLogin方法,例如
doLogin() {//1.定義urllet url = "http://localhost:9000/auth/oauth/token"http://2.定義參數let params = new URLSearchParams()params.append('username',this.username);params.append('password',this.password);params.append("client_id","gateway-client");params.append("client_secret","123456");params.append("grant_type","password");//3.發送異步請求axios.post(url, params).then((response) => {alert("login ok");let result=response.data;localStorage.setItem("accessToken",result.access_token);location.;}).catch((error)=>{console.log(error);})}資源服務器配置添加依賴打開資源服務的pom.xml文件 , 添加oauth2依賴 。
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency>令牌處理器配置package com.jt.auth.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;/** * 創建JWT令牌配置類,基于這個類實現令牌對象的創建和解析. * JWT令牌的構成有三部分構成: * 1)HEADER (頭部信息:令牌類型,簽名算法) * 2)PAYLOAD (數據信息-用戶信息,權限信息,令牌失效時間,...) * 3)SIGNATURE (簽名信息-對header和payload部分進行加密簽名) [email protected] class TokenConfig {//定義令牌簽發口令(暗號),這個口令自己定義即可//在對header和PAYLOAD部分進行簽名時,需要的一個口令private String SIGNING_KEY= "auth";//初始化令牌生成策略(默認生成策略 UUID)//這里我們采用JWT方式生成令牌@Beanpublic TokenStore tokenStore(){return new JwtTokenStore(jwtAccessTokenConverter());}//構建JWT令牌轉換器對象,基于此對象創建令牌,解析令牌@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter(){JwtAccessTokenConverter converter=new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}}資源服務令牌解析配置package com.jt.resource.config;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;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;import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.http.HttpServletResponse;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;@[email protected]@EnableGlobalMethodSecurity(prePostEnabled = true)public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;/*** token服務配置*/@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(tokenStore);}/*** 路由安全認證配置*/@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable();http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());http.authorizeRequests().anyRequest().permitAll();}//沒有權限時執行此處理器方法public AccessDeniedHandler accessDeniedHandler() {return (request, response, e) -> {Map<String, Object> map = new HashMap<>();map.put("state", HttpServletResponse.SC_FORBIDDEN);//SC_FORBIDDEN的值是403map.put("message", "沒有訪問權限,請聯系管理員");//1設置響應數據的編碼response.setCharacterEncoding("utf-8");//2告訴瀏覽器響應數據的內容類型以及編碼response.setContentType("application/json;charset=utf-8");//3獲取輸出流對象PrintWriter out=response.getWriter();//4 輸出數據String result=new ObjectMapper().writeValueAsString(map);out.println(result);out.flush();};}}資源服務令牌解析配置package com.jt.resource.config;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;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;import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.http.HttpServletResponse;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;@[email protected]@EnableGlobalMethodSecurity(prePostEnabled = true)public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Autowiredprivate TokenStore tokenStore;/*** token服務配置*/@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(tokenStore);}/*** 路由安全認證配置*/@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable();http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());http.authorizeRequests().anyRequest().permitAll();}//沒有權限時執行此處理器方法public AccessDeniedHandler accessDeniedHandler() {return (request, response, e) -> {Map<String, Object> map = new HashMap<>();map.put("state", HttpServletResponse.SC_FORBIDDEN);//SC_FORBIDDEN的值是403map.put("message", "沒有訪問權限,請聯系管理員");//1設置響應數據的編碼response.setCharacterEncoding("utf-8");//2告訴瀏覽器響應數據的內容類型以及編碼response.setContentType("application/json;charset=utf-8");//3獲取輸出流對象PrintWriter out=response.getWriter();//4 輸出數據String result=new ObjectMapper().writeValueAsString(map);out.println(result);out.flush();};}}ResourceController 方法配置在controller的上傳方法上添加 @PreAuthorize(“hasAuthority(‘sys:res:create’)”)注解 , 用于告訴底層框架方法此方法需要具備的權限 , 例如
@PreAuthorize("hasAuthority('sys:res:create')")@PostMapping("/upload/")public String uploadFile(MultipartFile uploadFile) throws IOException {...}啟動服務訪問測試第一步:啟動服務(sca-auth,sca-resource-gateway,sca-resource)第二步:執行登陸獲取access_token令牌第三步:攜帶令牌訪問資源(url中的前綴”sca”是在資源服務器中自己指定的,你的網關怎么配置的,你就怎么寫)設置請求頭(header) , 要攜帶令牌并指定請求的內容類型 , 例如
文件上傳JS方法設計
function upload(file){//定義一個表單let form=new FormData();//將圖片添加到表單中form.append("uploadFile",file);let url="http://localhost:9000/sca/resource/upload/";//異步提交方式1axios.post(url,form,{headers:{"Authorization":"Bearer "+localStorage.getItem("accessToken")}}).then(function (response){let result=response.data;if(result.state==403){alert(result.message);return;}alert("upload ok");})}技術摘要應用實踐說明背景分析企業中數據是最重要的資源,對于這些數據而言,有些可以直接匿名訪問,有些只能登錄以后才能訪問,還有一些你登錄成功以后,權限不夠也不能訪問.總之這些規則都是保護系統資源不被破壞的一種手段.幾乎每個系統中都需要這樣的措施對數據(資源)進行保護.我們通常會通過軟件技術對這樣業務進行具體的設計和實現.早期沒有統一的標準,每個系統都有自己獨立的設計實現,但是對于這個業務又是一個共性,后續市場上就基于共性做了具體的落地實現,例如Spring Security,Apache shiro , JWT , Oauth2等技術誕生了.
Spring Security 技術
Spring Security 是一個企業級安全框架,由spring官方推出,它對軟件系統中的認證,授權,加密等功能進行封裝,并在springboot技術推出以后,配置方面做了很大的簡化.現在市場上分布式架構中的安全控制 , 正在逐步的轉向Spring Security 。Spring Security 在企業中實現認證和授權業務時,底層構建了大量的過濾器 , 如圖所示:
其中:圖中綠色部分為認證過濾器,黃色部分為授權過濾器 。Spring Security就是通過這些過濾器然后調用相關對象一起完成認證和授權操作.Jwt 數據規范
JWT(JSON WEB Token)是一個標準 , 采用數據自包含方式進行json格式數據設計 , 實現各方安全的信息傳輸 , 其官方網址為:https://jwt.io/ 。官方JWT規范定義 , 它構成有三部分 , 分別為Header(頭部) , Payload(負載) , Signature(簽名),其格式如下:
xxxxx.yyyyy.zzzzzHeader 部分是一個 JSON 對象 , 描述 JWT 的元數據 , 通常是下面的樣子 。{"alg": "HS256","typ": "JWT"}上面代碼中 , alg屬性表示簽名的算法(algorithm) , 默認是 HMAC SHA256(簡寫HS256);typ屬性表示這個令牌(token)的類型(type) , JWT 令牌統一寫為JWT 。最后 , 將這個 JSON 對象使用 Base64URL 算法(詳見后文)轉成字符串 。Payload部分Payload 部分也是一個 JSON 對象 , 用來存放實際需要傳遞的數據 。JWT規范中規定了7個官方字段 , 供選用 。
iss (issuer):簽發人exp (expiration time):過期時間sub (subject):主題aud (audience):受眾nbf (Not Before):生效時間iat (Issued At):簽發時間jti (JWT ID):編號除了官方字段 , 你還可以在這個部分定義私有字段 , 下面就是一個例子 。
{"sub": "1234567890","name": "John Doe","admin": true}注意 , JWT 默認是不加密的 , 任何人都可以讀到 , 所以不要把秘密信息放在這個部分 。這個 JSON 對象也要使用 Base64URL 算法轉成字符串 。
Signature部分
Signature 部分是對前兩部分的簽名 , 其目的是防止數據被篡改 。
首先 , 需要指定一個密鑰(secret) 。這個密鑰只有服務器才知道 , 不能泄露給用戶 。然后 , 使用 Header 里面指定的簽名算法(默認是 HMAC SHA256) , 按照下面的公式產生簽名 。
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)算出簽名以后 , 把 Header、Payload、Signature 三個部分拼成一個字符串 , 每個部分之間用”點”(.)分隔 , 就可以返回給用戶 。Oauth2規范
oauth2定義了一種認證授權協議 , 一種規范 , 此規范中定義了四種類型的角色:
1)資源有者(User)
2)認證授權服務器(jt-auth)
3)資源服務器(jt-resource)
4)客戶端應用(jt-ui)
同時 , 在這種協議中規定了認證授權時的幾種模式:
1)密碼模式 (基于用戶名和密碼進行認證)
2)授權碼模式(就是我們說的三方認證:QQ , 微信 , 微博 , 。。。。)
3)…
總結(Summary)重難點分析單點登陸系統的設計架構(微服務架構)服務的設計及劃分(資源服務器 , 認證服務器 , 網關服務器 , 客戶端服務)認證及資源訪問的流程(資源訪問時要先認證再訪問)認證和授權時的一些關鍵技術(Spring Security,Jwt,Oauth2)FAQ 分析為什么要單點登陸(分布式系統 , 再訪問不同服務資源時 , 不要總是要登陸 , 進而改善用戶體驗)單點登陸解決方案?(市場常用兩種: spring security+jwt+oauth2,spring securit+redis+oauth2)Spring Security 是什么?(spring框架中的一個安全默認 , 實現了認證和授權操作)JWT是什么?(一種令牌格式 , 一種令牌規范 , 通過對JSON數據采用一定的編碼 , 加密進行令牌設計)OAuth2是什么?(一種認證和授權規范 , 定義了單點登陸中服務的劃分方式 , 認證的相關類型)
…
【實現單點登錄 實現單點登錄的三種類型】Bug 分析401 : 訪問資源時沒有認證 。403 : 訪問資源時沒有權限 。404:訪問的資源找不到(一定要檢查你訪問資源的url)405: 請求方式不匹配(客戶端請求方式是GET , 服務端處理請求是Post就是這個問題)500: 不看后臺無法解決?(error,warn)…
- 阿里云管理控制臺在哪 阿里云管理控制臺登錄
- 設計網頁游戲 網絡游戲網站的設計與實現
- c語言大小寫轉換的程序 C語言實現大小寫轉換
- hbase構建二級索引的實現方式有哪些 hbase solr二級索引
- 什么是響應式網頁,如何實現 html響應式網頁設計代碼范文
- js如何將json字符串轉成json對象 如何實現json形式的字符串和js對象之間的相互轉換
- 阿里郵箱企業版手機版怎么改密碼 阿里郵箱企業版手機版登錄
- 163郵箱登陸注冊入口 163郵箱登錄注冊
- uc賬號中心登錄 uc個人登陸中心
- 企業郵政登錄 企業郵局郵箱
