Java spring單點登錄系統
1.單點登錄系統介紹
多點登陸系統。應用起來相對繁瑣(每次訪問資源服務都需要重新登陸認證和授權)。與此同時,系統代碼的重復也比較高。所以單點登錄系統,倍受歡迎!
單點登錄系統,即多個站點共用一臺認證授權服務器,用戶在其中任何一個站點登錄後,可以免登錄訪問其他所有站點。而且,各站點間可以通過該登錄狀態直接交互。
2.簡單業務實現
在文件上傳的項目添加認證授權服務,義登錄頁面(login.html),然後在頁面中輸入自己的登陸賬號,登陸密碼,將請求提交給網關,然後網關將請求轉發到auth工程,登陸成功和失敗要返回json數據,按照如下結構實現
在02-sca工程創建 sca-auth子module,作為認證授權服務
2.1添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
2.2 項目配置文件
在sca-auth工程中創建bootstrap.yml文件
server: port: 8071 spring: application: name: sca-auth cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848
2.3添加項目啟動類
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceAuthApplication { public static void main(String[] args) { SpringApplication.run(ResourceAuthApplication.class, args); } }
2.4 啟動並訪問項目
項目啟動時,系統會默認生成一個登陸密碼
打開瀏覽器輸入http://localhost:8071呈現登陸頁面
默認用戶名為user,密碼為系統啟動時,在控制臺呈現的密碼。執行登陸測試,登陸成功進入如下界面(因為沒有定義登陸頁面,所以會出現404)
3. 優化進一步設計
3.1 定義安全配置類 SecurityConfig
修改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.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.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; @Configuration//配置對象--系統啟動時底層會產生代理對象,來初始化一些對象 public class SecurityConfig extends WebSecurityConfigurerAdapter { //WebSecurityConfigurerAdapter 類是個適配器, 在配置的時候,需要我們自己寫個配置類去繼承他,然後編寫自己所特殊需要的配置 //BCryptPasswordEncoder密碼加密對象比MD5安全性更高,MD5暴力反射可以破解過 @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 配置認證管理器(負責對客戶輸入的用戶信息進行認證),在其他配置類中會用到這個對象 * @return * @throws Exception */ @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /**在這個方法中定義登錄規則 * 1)對所有請求放行(當前工程隻做認證) * 2)登錄成功信息的返回 * 3)登錄失敗信息的返回 * */ @Override protected void configure(HttpSecurity http) throws Exception { //禁用跨域 http.csrf().disable(); //放行所有請求 http.authorizeRequests().anyRequest().permitAll(); //登錄成功與失敗的處理 http.formLogin() .successHandler(successHandler()) // .successHandler(AuthenticationSuccessHandler對象) .failureHandler(failureHandler()); } @Bean //構建successHandler()方法來創建AuthenticationSuccessHandler對象 public 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); }; } @Bean //failureHandler();方法來創建AuthenticationSuccessHandler對象 public 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); }; } //提取公共代碼,將對象轉為Json傳給客戶端, 構建writeJsonToClient(); private void writeJsonToClient(HttpServletResponse response, Object object) throws IOException { // Object 類型,不隻是Map類型,說不準 //2.將對象轉換為json //Gson-->toJson (需要自己找依賴) //fastjson-->JSON (spring-cloud-starter-alibaba-sentinel) //jackson-->writeValueAsString (spring-boot-starter-web) String jsonStr=new ObjectMapper().writeValueAsString(object); //3.將json字符串寫到客戶端 PrintWriter writer = response.getWriter(); writer.println(jsonStr); writer.flush(); } }
3.2定義用戶信息處理對象
正常來說,用來與數據庫中的用戶信息作對比,認證是否正確,可否授權
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方法, * 頁面上輸入的用戶名會傳給這個方法的參數 * */ @Service public class UserDetailsServiceImpl implements UserDetailsService {//獲取用戶詳細信息的接口 @Autowired //BCryptPasswordEncoder密碼加密對象 private BCryptPasswordEncoder passwordEncoder; //UserDetails用戶封裝用戶信息(認證和權限信息) @Override //重寫UserDetailsService 接口中的 loadUserByUsername();方法,定義用來核對數據庫數據和授於相應的權限 public 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); } }
3.3 網關中登陸路由配置
在網關配置文件中添加如下配置
server: port: 9001 spring: application: name: sca-resource-gateway cloud: sentinel: #限流設計 transport: dashboard: localhost:8180 eager: true nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 file-extension: yml gateway: discovery: locator: enabled: true routes: - id: router02 uri: lb://sca-auth #lb表示負載均衡,底層默認使用ribbon實現 predicates: #定義請求規則(請求需要按照此規則設計) - Path=/auth/login/** #請求路徑設計,單體架構 filters: - StripPrefix=1 #轉發之前去掉path中第一層路徑
3.4基於Postman進行訪問測試
啟動sca-gateway,sca-auth服務,然後基於postman進行訪問測試
3.5 定義登陸頁面
在sca-resource-ui工程的static目錄中定義login-sso.html 登陸頁面
<!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 href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="external nofollow" 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="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var vm=new Vue({ el:"#app",//定義監控點,vue底層會基於此監控點在內存中構建dom樹 data:{ //此對象中定義頁面上要操作的數據 username:"", password:"" }, methods: {//此位置定義所有業務事件處理函數 doLogin() { //1.定義url let url = "http://localhost:9001/auth/oauth/token" //2.定義參數 let params = new URLSearchParams() params.append('username',this.username); params.append('password',this.password); params.append("client_id","gateway-client"); params.append("grant_type","password"); params.append("client_secret","123456"); //3.發送異步請求 axios.post(url, params).then((response) => { debugger console.log(response.data); let result=response.data; // localStorage.setItem("accessToken",result.access_token); location.href="/fileupload.html" rel="external nofollow" }) } } }); </script> </body> </html>
3.6 構建令牌配置對象
借助JWT(Json Web Token-是一種json格式)方式將用戶信息轉換為json格式,然後進行加密,保存用戶信息到客戶端,然後發送在客戶端客戶端接收到這個JWT之後,保存在客戶端,之後帶著JWT訪問其它模塊時,資源服務器解析獲得用戶信息,進行訪問,達到解放內存的目的
config 目錄下 TokenConfig類
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類型令牌 *構建令牌的構成有三部分: * header(頭部信息:令牌類型)/ * payload(數據信息-用戶信息,權限信息)/ * SIGNATURE(簽名信息-對header和payload部分加密) * */ @Configuration //配置對象--系統啟動時底層會產生代理對象,來初始化一些對象 public class TokenConfig { //定義令牌簽發口令(暗號,規則),解密口令 //當客戶端在執行登錄時,加入有攜帶這個信息,認證服務器可以簽發令牌 private String SIGNING_KEY = "auth"; 在對header和payload簽名時,需要的一個口令 //構建令牌生成器對象() @Bean public TokenStore tokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter());//令牌生成器(jwt轉換器) } @Bean //Jwt轉換器,將任何數據轉換為jwt字符串 public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter=new JwtAccessTokenConverter(); //設置加密/解密口令 converter.setSigningKey(SIGNING_KEY); return converter; } }
創建認證管理器對象
在SecurityConfig中添加如下方法(後面授權服務器會用到):
/** * 配置認證管理器(負責對客戶輸入的用戶信息進行認證),在其他配置類中會用到這個對象 * @return * @throws Exception */ @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
3.7 定義認證授權核心配置
授權服務器的核心配置
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(暫時不說) */ @AllArgsConstructor @Configuration @EnableAuthorizationServer //開啟認證和授權服務 public class Oauth2Config extends AuthorizationServerConfigurerAdapter { //此對象負責完成認證管理 private AuthenticationManager authenticationManager; //TokenStore負責完成令牌創建,信息讀取 private TokenStore tokenStore; //負責獲取用戶詳情信息(username,password,client_id,grant_type,client_secret) //private ClientDetailsService clientDetailsService; //JWT令牌轉換器(基於用戶信息構建令牌,解析令牌) private JwtAccessTokenConverter jwtAccessTokenConverter; //密碼加密匹配器對象 private PasswordEncoder passwordEncoder; //負責獲取用戶信息信息 private UserDetailsService userDetailsService; //設置認證端點的配置(/oauth/token),客戶端通過這個路徑獲取JWT令牌 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints //配置認證管理器 .authenticationManager(authenticationManager) //驗證用戶的方法獲得用戶詳情 .userDetailsService(userDetailsService) //要求提交認證使用post請求方式,提高安全性 .allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET) //要配置令牌的生成,由於令牌生成比較復雜,下面有方法實現 .tokenServices(tokenService());//這個不配置,默認令牌為UUID.randomUUID().toString() } //定義令牌生成策略 @Bean public AuthorizationServerTokenServices tokenService(){ //這個方法的目標就是獲得一個令牌生成器 DefaultTokenServices services=new DefaultTokenServices(); //支持令牌刷新策略(令牌有過期時間) services.setSupportRefreshToken(true); //設置令牌生成策略(tokenStore在TokenConfig配置瞭,本次我們應用JWT-定義瞭一種令牌格式) services.setTokenStore(tokenStore); //設置令牌增強(固定用法-令牌Payload部分允許添加擴展數據,例如用戶權限信息) TokenEnhancerChain chain=new TokenEnhancerChain(); chain.setTokenEnhancers( Arrays.asList(jwtAccessTokenConverter)); //令牌增強對象設置到令牌生成 services.setTokenEnhancer(chain); //設置令牌有效期 services.setAccessTokenValiditySeconds(3600);//1小時 //刷新令牌應用場景:一般在用戶登錄系統後,令牌快過期時,系統自動幫助用戶刷新令牌,提高用戶的體驗感 services.setRefreshTokenValiditySeconds(3600*72);//3天 //配置客戶端詳情 //services.setClientDetailsService(clientDetailsService); return services; } //設置客戶端詳情類似於用戶詳情 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() //客戶端id .withClient("gateway-client") //客戶端秘鑰 .secret(passwordEncoder.encode("123456")) //設置權限 .scopes("all")//all隻是個名字而已和寫abc效果相同 //允許客戶端進行的操作 裡面的字符串千萬不能寫錯 .authorizedGrantTypes("password","refresh_token"); } // 認證成功後的安全約束配置 @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { //認證通過後,允許客戶端進行哪些操作 security //公開oauth/token_key端點 .tokenKeyAccess("permitAll()") //公開oauth/check_token端點 .checkTokenAccess("permitAll()") //允許提交請求進行認證(申請令牌) .allowFormAuthenticationForClients(); } }
Postman訪問測試
第一步:啟動服務
依次啟動sca-auth服務,sca-resource-gateway,sca-resource-ui服務。
第二步:檢測sca-auth服務控制臺的Endpoints信息,例如:
打開postman進行登陸訪問測試
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzAwNzYxMTAsInVzZXJfbmFtZSI6ImphY2siLCJhdXRob3JpdGllcyI6WyJzeXM6cmVzOmNyZWF0ZSIsInN5czpyZXM6cmV0cmlldmUiXSwianRpIjoiM2Q0MzExOTYtYmRkZi00Y2NhLWFmMDMtNWMzNGM4ZmJkNzQ3IiwiY2xpZW50X2lkIjoiZ2F0ZXdheS1jbGllbnQiLCJzY29wZSI6WyJhbGwiXX0.GnrlqsZMSdagDaRQDZWDLbY7I7KUlXQgyXATcXXS6FI", "token_type": "bearer",
4 資源服務器配置–sca-resource
4.1 構建令牌配置對象
package com.jt.resource.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 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類型令牌 *構建令牌的構成有三部分: * header(頭部信息:令牌類型)/ * payload(數據信息-用戶信息,權限信息)/ * SIGNATURE(簽名信息-對header和payload部分加密) * */ @Configuration //配置對象--系統啟動時底層會產生代理對象,來初始化一些對象 public class TokenConfig { //定義令牌簽發口令(暗號,規則),解密口令 //當客戶端在執行登錄時,加入有攜帶這個信息,認證服務器可以簽發令牌 //在對header和payload簽名時 private String SIGNING_KEY = "auth"; //構建令牌生成器對象() @Bean public TokenStore tokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter());//令牌生成器(轉換器) } //Jwt轉換器,將任何數據轉換為jwt字符串 @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter converter=new JwtAccessTokenConverter(); //設置加密/解密口令 converter.setSigningKey(SIGNING_KEY); return converter; } }
4.2 資源服務令牌解析配置
2.將對象轉換為json //Gson-->toJson (需要自己找依賴) //fastjson-->JSON (spring-cloud-starter-alibaba-sentinel) //jackson-->writeValueAsString (spring-boot-starter-web)
package com.jt.resource.config; import com.alibaba.fastjson.JSON; import com.google.gson.Gson; import com.fasterxml.jackson.databind.ObjectMapper; 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.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.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** * 資源服務器的配置,在這個對象 * 1)JWT 令牌解析配置(客戶端帶著令牌訪問資源時,要對令牌進行解析) * 2) 啟動資源訪問的授權配置(不是所有登陸用戶可以訪問所有資源) */ @Configuration @EnableResourceServer 此註解會啟動資源服務器的默認配置 @EnableGlobalMethodSecurity(prePostEnabled = true) //執行方法之前啟動權限檢查 public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired private TokenStore tokenStore; /** * token服務配置 */ @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { //super.configure(resources); //定義令牌生成策略,這裡不是要創建令牌,是要解析令牌 resources.tokenStore(tokenStore); } /** * 路由安全認證配置 */ @Override public void configure(HttpSecurity http) throws Exception { //super.configure(http); http.csrf().disable();//關閉跨域攻擊 //放行所有的資源訪問(對資源的方問不做認證) http.authorizeRequests().anyRequest().permitAll(); //http.authorizeRequests().mvcMatchers("/resource/**") // .authenticated(); //假如沒有認證就訪問會報401異常 //處理異常 http.exceptionHandling() .accessDeniedHandler(accessDeniedHandler()); //403的異常處理 .拒絕訪問的處理(AccessDeniedHandler類型對象) } @Bean public AccessDeniedHandler accessDeniedHandler() { //返回 AccessDeniedHandler對象 return (request,response, exception)->{ Map<String,Object> map = new HashMap<>(); map.put("state", HttpServletResponse.SC_FORBIDDEN);//403 map.put("message", "抱歉,沒有這個資源"); //1設置響應數據的編碼 response.setCharacterEncoding("utf-8"); //2告訴瀏覽器響應數據的內容類型以及編碼 response.setContentType("application/json;charset=utf-8"); //2.將對象轉換為json //1.fastjson-->JSON (spring-cloud-starter-alibaba-sentinel) //String jsonStr= JSON.toJSONString(map);//fastjson //2.Gson-->toJson (需要自己找依賴) Gson gson = new Gson(); String jsonStr = gson.toJson(map); //jackson-->writeValueAsString (spring-boot-starter-web) //String jsonStr = new ObjectMapper().writeValueAsString(map); PrintWriter writer = response.getWriter(); writer.println(jsonStr); writer.flush(); }; } }
4.3 設置資源訪問的權限
在ResourceController的上傳方法上添加 @PreAuthorize(“hasAuthority(‘sys:res:create’)”)註解,用於告訴底層框架方法此方法需要具備的權限,
@PreAuthorize("hasAuthority('sys:res:create')") @PostMapping("/upload/") public String uploadFile(MultipartFile uploadFile) throws IOException { ... }
不加權限,會報403異常,並且展示我們修改403異常的信息返回在控制臺上
4.4 啟動服務訪問測試
4.4.1 訪問auth認證授權服務獲取token
啟動服務(sca-auth,sca-resource-gateway,sca-resource)
執行POSTMAN ,訪問 auth認證授權服務 http://localhost:9001/auth/oauth/token, 獲取token
4.4.2 攜帶TOKEN訪問資源服務器
復制access_token ,請求地址: http://localhost:9001/sca/resource/upload/
1.設置請求頭(header),要攜帶令牌並指定請求的內容類型
2. 設置請求體(body),設置form-data,key要求為file類型,參數名與你服務端controller文件上傳方法的參數名相同,值為你選擇的文件
4.4.3 對403異常前端頁面顯示
function upload(file){ //定義一個表單 let form=new FormData(); //將圖片添加到表單中 form.append("uploadFile",file); let url="http://localhost:9000/sca/resource/upload/"; //異步提交方式1 axios.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"); }) }
1.啟動服務(sca-auth,sca-resource-gateway,sca-resource)
2.執行登陸 localhost:8080/login-sso.html 獲取access_token令牌
3.攜帶令牌訪問資源(url中的前綴”sca”是在資源服務器中自己指定的,你的網關怎麼配置的,你就怎麼寫)
成功:
403異常,沒有訪問權限
4.5 Oauth2規范
oauth2定義瞭一種認證授權協議,一種規范,此規范中定義瞭四種類型的角色:
1)資源有者(User)
2)認證授權服務器(jt-auth)
3)資源服務器(jt-resource)
4)客戶端應用(jt-ui)
同時,在這種協議中規定瞭認證授權時的幾種模式:
1)密碼模式 (基於用戶名和密碼進行認證)
2)授權碼模式(就是我們說的三方認證:QQ,微信,微博,。。。。)
3)…
4.6 面試問題點
單點登陸系統的設計架構(微服務架構)
服務的設計及劃分(資源服務器,認證服務器,網關服務器,客戶端服務)
認證及資源訪問的流程(資源訪問時要先認證再訪問)
認證和授權時的一些關鍵技術(Spring Security,Jwt,Oauth2)
FAQ 分析
為什麼要單點登陸(分佈式系統,再訪問不同服務資源時,不要總是要登陸,進而改善用戶體驗)
單點登陸解決方案?(市場常用兩種: spring security+jwt+oauth2,spring securit+redis+oauth2)
Spring Security 是什麼?(spring框架中的一個安全默認,實現瞭認證和授權操作)
JWT是什麼?(一種令牌格式,一種令牌規范,通過對JSON數據采用一定的編碼,加密進行令牌設計)
OAuth2是什麼?(一種認證和授權規范,定義瞭單點登陸中服務的劃分方式,認證的相關類型)
…
5 Bug 分析
401 : 訪問資源時沒有認證。
403 : 訪問資源時沒有權限。
404:訪問的資源找不到(一定要檢查你訪問資源的url)
405: 請求方式不匹配(客戶端請求方式是GET,服務端處理請求是Post就是這個問題)
500: 不看後臺無法解決?(error,warn)
…
到此這篇關於Java spring單點登錄系統的文章就介紹到這瞭,更多相關Java單點登錄內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Springboot開發OAuth2認證授權與資源服務器操作
- SpringSecurityOAuth2 如何自定義token信息
- SpringSecurity OAtu2+JWT實現微服務版本的單點登錄的示例
- 詳解spring cloud ouath2中的資源服務器
- 教你用Java SpringBoot如何解決跨域