SpringSecurity OAtu2+JWT實(shí)現(xiàn)微服務(wù)版本的單點(diǎn)登錄的示例
何為單點(diǎn)登錄
單點(diǎn)登錄通俗的話來講在微服務(wù)當(dāng)中,在一個(gè)服務(wù)登錄后就能免去另一個(gè)服務(wù)的登錄操作,所謂單點(diǎn)登錄.
就好像你在微博總網(wǎng)站里登錄后,然后在微博里面的某一個(gè)模塊點(diǎn)進(jìn)去后,就發(fā)現(xiàn)這個(gè)模塊竟然不用登錄了,不是因?yàn)檫@個(gè)模塊與主網(wǎng)站是一體的用一個(gè)SpringSecurity就可以搞定了,這里面的水深著呢!
感興趣更深這個(gè)SpringSecurity建議去看看圖靈課堂的SpringSecurity,建議不要看尚硅谷的,那個(gè)版本說實(shí)話感覺有點(diǎn)老式了,教你手寫,其實(shí)我覺得,你既然學(xué)到了這個(gè)層次了,就應(yīng)該能建立起快速打通一個(gè)新技術(shù)的能力,這個(gè)"打通"的意思不是要你深入,而是會(huì)用!別想著深入,因?yàn)闆]有什么實(shí)際意義,現(xiàn)在不是我們小白應(yīng)該做的事,我們小白只要打好基礎(chǔ)就可以了,比如java,jvm,spring,mysql這些!
沒有SpringSecurity基礎(chǔ)就別看這篇文章了,你可能看得懂,但是你肯定實(shí)現(xiàn)不出來,不信我你就看
認(rèn)證中心
新建一個(gè)微服務(wù)模塊,可以另外建項(xiàng)目,也可以直接在你微服務(wù)項(xiàng)目里建立模塊就可以了.
maven配置
? <dependencies> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.springframework.boot</groupId> ? ? ? ? ? ? <artifactId>spring-boot-starter-thymeleaf</artifactId> ? ? ? ? </dependency> ? ? ? ? <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>org.springframework.cloud</groupId> ? ? ? ? ? ? <artifactId>spring-cloud-starter-security</artifactId> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.springframework.boot</groupId> ? ? ? ? ? ? <artifactId>spring-boot-starter-test</artifactId> ? ? ? ? ? ? <scope>test</scope> ? ? ? ? ? ? <exclusions> ? ? ? ? ? ? ? ? <exclusion> ? ? ? ? ? ? ? ? ? ? <groupId>org.junit.vintage</groupId> ? ? ? ? ? ? ? ? ? ? <artifactId>junit-vintage-engine</artifactId> ? ? ? ? ? ? ? ? </exclusion> ? ? ? ? ? ? </exclusions> ? ? ? ? </dependency> ? ? </dependencies>
用戶登錄邏輯
@Component public class SheepUserDetailsService implements UserDetailsService { ? ? @Autowired ? ? private PasswordEncoder passwordEncoder; ? ? @Override ? ? public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { ? ? ? ? if( !"admin".equals(s) ) ? ? ? ? ? ? throw new UsernameNotFoundException("用戶" + s + "不存在" ); ? ? ? ? return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL,ROLE_MEDIUM")); ? ? } }
OAtuh2配置
配置服務(wù)中心
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ? ? @Autowired ? ? AuthenticationManager authenticationManager; ? ? @Autowired ? ? SheepUserDetailsService sheepUserDetailsService; ? ? @Override ? ? public void configure(ClientDetailsServiceConfigurer clients) throws Exception { ? ? ? ? // 定義了兩個(gè)客戶端應(yīng)用的通行證 ? ? ? ? clients.inMemory() ? ? ? ? ? ? ? ? .withClient("admin") ? ? ? ? ? ? ? ? .secret(new BCryptPasswordEncoder().encode("123456")) ? ? ? ? ? ? ? ? .authorizedGrantTypes("authorization_code", "refresh_token","password") ? ? ? ? ? ? ? ? .scopes("all") ? ? ? ? ? ? ? ? .autoApprove(true) ? ? ? ? ? ? ? ? .redirectUris("http://192.168.216.1:8001/login","http://192.168.216.1:8004/login"); ? ? } ? ? @Override ? ? public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { ? ? ? ? endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); ? ? ? ? DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices(); ? ? ? ? tokenServices.setTokenStore(endpoints.getTokenStore()); ? ? ? ? tokenServices.setSupportRefreshToken(true); ? ? ? ? tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); ? ? ? ? tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); ? ? ? ? tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1)); // 一天有效期 ? ? ? ? endpoints.tokenServices(tokenServices); ? ? ? ? //密碼模式配置 ? ? ? ? endpoints.authenticationManager(authenticationManager).userDetailsService(sheepUserDetailsService); ? ? } ? ? @Override ? ? public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { ? ? ? ? security ? ? ? ? ? ? ? ? .tokenKeyAccess("isAuthenticated()") ? ? ? ? ? ? ? ? .checkTokenAccess("permitAll()") ? ? ? ? ? ? ? ? .allowFormAuthenticationForClients(); ? ? } ? ? @Bean ? ? public TokenStore jwtTokenStore() { ? ? ? ? return new JwtTokenStore(jwtAccessTokenConverter()); ? ? } ? ? @Bean ? ? public JwtAccessTokenConverter jwtAccessTokenConverter(){ ? ? ? ? JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); ? ? ? ? converter.setSigningKey("testKey"); ? ? ? ? return converter; ? ? } }
配置規(guī)則中心
Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { ? ? @Override ? ? @Bean ? ? public AuthenticationManager authenticationManager() throws Exception { ? ? ? ? return super.authenticationManager(); ? ? } ? ? @Autowired ? ? private UserDetailsService userDetailsService; ? ? @Bean ? ? public PasswordEncoder passwordEncoder() { ? ? ? ? return new BCryptPasswordEncoder(); ? ? } ? ? @Autowired ? ? private CustomLogoutSuccessHandler customLogoutSuccessHandler; ? ? @Bean ? ? public DaoAuthenticationProvider authenticationProvider() { ? ? ? ? DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); ? ? ? ? authenticationProvider.setUserDetailsService(userDetailsService); ? ? ? ? authenticationProvider.setPasswordEncoder(passwordEncoder()); ? ? ? ? authenticationProvider.setHideUserNotFoundExceptions(false); ? ? ? ? return authenticationProvider; ? ? } ? ? @Override ? ? protected void configure(HttpSecurity http) throws Exception { ? ? ? ? http.requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**","/uac/oauth/token","/remove") ? ? ? ? ? ? ? ? .and() ? ? ? ? ? ? ? ? .authorizeRequests() ? ? ? ? ? ? ? ? .antMatchers("/oauth/**").authenticated() ? ? ? ? ? ? ? ? .and() ? ? ? ? ? ? ? ? .formLogin().permitAll() ? ? ? ? ? ? ? ? .and() ? ? ? ? ? ? ? ? .logout() ? ? ? ? ? ? ? ? .logoutSuccessHandler(customLogoutSuccessHandler) ? ? ? ? ? ? ? ? // 無效會(huì)話 ? ? ? ? ? ? ? ? .invalidateHttpSession(true) ? ? ? ? ? ? ? ? // 清除身份驗(yàn)證 ? ? ? ? ? ? ? ? .clearAuthentication(true) ? ? ? ? ? ? ? ? .permitAll(); ? ? } ? ? @Override ? ? protected void configure(AuthenticationManagerBuilder auth) throws Exception { ? ? ? ? auth.authenticationProvider(authenticationProvider()); ? ? } }
@Component public class CustomLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler { ? ? @Override ? ? public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { ? ? ? ? // 將子系統(tǒng)的cookie刪掉 ? ? ? ? //建議將token也刪除,直接寫個(gè)controller接口就可以了,可以在前端調(diào)用/logout的同時(shí)調(diào)用刪除token接口 ? ? ? ? Cookie[] cookies = request.getCookies(); ? ? ? ? if(cookies != null && cookies.length>0){ ? ? ? ? ? ? for (Cookie cookie : cookies){ ? ? ? ? ? ? ? ? cookie.setMaxAge(0); ? ? ? ? ? ? ? ? cookie.setPath("/"); ? ? ? ? ? ? ? ? response.addCookie(cookie); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? super.handle(request, response, authentication); ? ? } }
請(qǐng)求模塊
下面這個(gè)是請(qǐng)求模塊,也就是獨(dú)立出一個(gè)微服務(wù),假如這個(gè)微服務(wù)是做業(yè)務(wù)的,會(huì)給認(rèn)證中心發(fā)出請(qǐng)求,然后去熱證,這個(gè)模塊也算登錄了.
那么有道友會(huì)問:其他模塊在該模塊登錄后還要登錄嗎?答案:不用!
至于要以什么為基礎(chǔ),接著往下看:
請(qǐng)求模塊這個(gè)依賴很關(guān)鍵
? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.springframework.cloud</groupId> ? ? ? ? ? ? <artifactId>spring-cloud-starter-oauth2</artifactId> ? ? ? ? </dependency> ? ? ? ? <dependency> ? ? ? ? ? ? <groupId>org.springframework.cloud</groupId> ? ? ? ? ? ? <artifactId>spring-cloud-starter-security</artifactId> ? ? ? ? </dependency>
這個(gè)yml也很重要
auth-server: http://localhost:8085/uac security: oauth2: client: client-id: admin client-secret: 123456 user-authorization-uri: ${auth-server}/oauth/authorize #認(rèn)證 access-token-uri: ${auth-server}/oauth/token #獲取token resource: jwt: key-uri: ${auth-server}/oauth/token_key #忘了,反正要寫上
上面的認(rèn)證和獲取token這兩個(gè)配置很重要,還有一個(gè)/oauth/check_token,這個(gè)是用來檢查token是否合法的,這些都怎么用,是什么,后面會(huì)說
下面這個(gè)也很重要
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableOAuth2Sso public class ClientWebsecurityConfigurer extends WebSecurityConfigurerAdapter { ? ? @Override ? ? public void configure(HttpSecurity http) throws Exception { ? ? //下面表示該模塊所有請(qǐng)求都要攔截 ? ? //因?yàn)樵趛ml配置了認(rèn)證中心的各個(gè)路徑,所以會(huì)自動(dòng)跳轉(zhuǎn)到認(rèn)證中心去認(rèn)證 ? ? //如果你想在當(dāng)前模塊排除哪些攔截,可以在下面去排除 ? ? ? ? http.antMatcher("/**").authorizeRequests() ? ? ? ? ? ? ? ? .anyRequest().authenticated(); ? ? } }
真實(shí)請(qǐng)求
下面就可以正式去請(qǐng)求了,這里很多網(wǎng)友都會(huì)有疑問,就是前面我在認(rèn)證中心授予了權(quán)限之后,在其他模塊該如何去權(quán)限的規(guī)定呢?其實(shí)很簡(jiǎn)單,有很多博主都沒有說,直接加上注解就可以了.
? ? @GetMapping("/get") ? ? @PreAuthorize("hasAuthority('ROLE_NORMAL')") ? ? public String get(HttpServletRequest request){ ? ? ? ? System.out.println("函數(shù)進(jìn)來了"); ? ? ? ? return "uusb1j"; ? ? } }
這個(gè)時(shí)候,如果該微服務(wù)的get請(qǐng)求過來,就會(huì)跳轉(zhuǎn)到認(rèn)證中心去認(rèn)證.
一些小問題
這個(gè)時(shí)候只要有相同配置的模塊都不用自行登錄了.如果有一個(gè)模塊進(jìn)行登出請(qǐng)求,所有服務(wù)都會(huì)進(jìn)行登出.
注意認(rèn)證中心有項(xiàng)配置redirectUris(“http://192.168.216.1:8001/login”,“http://192.168.216.1:8004/login”),這個(gè)配置代表認(rèn)證成功后重定向去哪個(gè)地址,但并不會(huì)真的重定向去對(duì)應(yīng)模塊的login頁,而是重定向去你請(qǐng)求前被攔截的地址,但是這個(gè)有講究就是必須配置請(qǐng)求前所在模塊的地址,每個(gè)模塊對(duì)應(yīng)一個(gè)重定向地址,最好是重定向到對(duì)應(yīng)模塊的/login頁,其他頁也行.如果你從網(wǎng)關(guān)過來,然后你這里重定向回網(wǎng)關(guān)是不行的,除非你網(wǎng)關(guān)有相關(guān)操作,不然你網(wǎng)關(guān)只是一個(gè)轉(zhuǎn)發(fā)功能,是先轉(zhuǎn)發(fā)到對(duì)應(yīng)模塊,然后發(fā)現(xiàn)該模塊的某個(gè)地址不可訪問,才去認(rèn)證中心請(qǐng)求權(quán)限的,所以這里的重定向地址還是具體到對(duì)應(yīng)模塊才對(duì),其他不行,有多個(gè)用","隔開就行.
到此這篇關(guān)于SpringSecurity OAtu2+JWT實(shí)現(xiàn)微服務(wù)版本的單點(diǎn)登錄的示例的文章就介紹到這了,更多相關(guān)SpringSecurity OAtu2 JWT單點(diǎn)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MAC算法之消息摘要算法HmacMD5的實(shí)現(xiàn)
這篇文章主要介紹了MAC算法之消息摘要算法HmacMD5的實(shí)現(xiàn)的相關(guān)資料,這里提供實(shí)例,幫助大家學(xué)習(xí)理解這部分知識(shí),需要的朋友可以參考下2017-08-08spring?boot學(xué)習(xí)筆記之操作ActiveMQ指南
ActiveMQ是一種開源的基于JMS規(guī)范的一種消息中間件的實(shí)現(xiàn),ActiveMQ的設(shè)計(jì)目標(biāo)是提供標(biāo)準(zhǔn)的,面向消息的,能夠跨越多語言和多系統(tǒng)的應(yīng)用集成消息通信中間件,這篇文章主要給大家介紹了關(guān)于spring?boot學(xué)習(xí)筆記之操作ActiveMQ指南的相關(guān)資料,需要的朋友可以參考下2021-11-11Java實(shí)現(xiàn)經(jīng)典捕魚達(dá)人游戲的示例代碼
《捕魚達(dá)人》是一款以深海狩獵為題材的休閑競(jìng)技游戲。本文將利用Java實(shí)現(xiàn)這一經(jīng)典的游戲,文中采用了swing技術(shù)進(jìn)行了界面化處理,需要的可以參考一下2022-02-02解決springboot自定義注解AOP在controller上導(dǎo)致controller注入失敗問題
這篇文章主要介紹了解決springboot自定義注解AOP在controller上導(dǎo)致controller注入失敗問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10Java根據(jù)實(shí)體生成SQL數(shù)據(jù)庫表的示例代碼
這篇文章主要來和大家分享一個(gè)Java實(shí)現(xiàn)根據(jù)實(shí)體生成SQL數(shù)據(jù)庫表的代碼,文中的實(shí)現(xiàn)代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-07-07java數(shù)據(jù)類型與二進(jìn)制詳細(xì)介紹
這篇文章主要介紹了java數(shù)據(jù)類型與二進(jìn)制詳細(xì)介紹的相關(guān)資料,這里對(duì)數(shù)據(jù)類型進(jìn)行了一一介紹分析,并說明自動(dòng)轉(zhuǎn)換和強(qiáng)制轉(zhuǎn)換,需要的朋友可以參考下2017-07-07高效數(shù)據(jù)傳輸?shù)拿孛芪淦鱌rotobuf的使用教程
Protobuf(Protocol?Buffers)是由?Google?開發(fā)的一種輕量級(jí)、高效的數(shù)據(jù)交換格式,它被用于結(jié)構(gòu)化數(shù)據(jù)的序列化、反序列化和傳輸,本文主要介紹了它的具體使用方法,需要的可以參考一下2023-05-05