<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Spring Cloud下基于OAUTH2認證授權的實現示例 在`Spring Cloud`需要使用`OAUTH2`來實現多個微服務的統一認證授權,通過向`OAUTH服務`發送某個類型的`grant type`進行集中認證和授權,從而獲得`token`,而這個token是受其他微服務信任的,我們在后續的訪問可以通過`token`來進行,從而實現了微服務的統一認證授權。 **本示例提供了四大部分:** 1. `discovery-service`:服務注冊和發現的基本模塊 2. `auth-server`:OAUTH2認證授權中心 3. `order-service`:普通微服務,用來驗證認證和授權 4. `api-gateway`:邊界網關(所有微服務都在它之后) **OAUTH2中的角色:** 1. `Resource Server`:被授權訪問的資源 2. `Authotization Server`:OAUTH2認證授權中心 3. `Resource Owner`: 用戶 4. `Client`:使用API的客戶端(如Android 、IOS、web app) Grant Type: 1. `Authorization Code`:用在服務端應用之間 2. `Implicit`:用在移動app或者web app(這些app是在用戶的設備上的,如在手機上調起微信來進行認證授權) 3. `Resource Owner Password Credentials(password)`:應用直接都是受信任的(都是由一家公司開發的,本例子使用 4. `Client Credentials`:用在應用API訪問。 5. 【擴展pwd】MobilePassword 手機號密碼 6. 【擴展sms】MobileSms 手機號短信驗證碼 環境搭建: 1、基礎環境 ``` mysql oauth\_client\_details 表存儲客戶端信息 redis 存儲短信驗證碼信息 ``` 2、auth-server搭建 ***** \*\*2.1 \*\*定義攔截器攔截登錄的請求【在這個類種主要完成2部分工作:1、根據參數獲取當前的是認證類型,2、根據不同的認證類型調用不同的IntegrationAuthenticator.prepar進行預處理】 ~~~ package com.hjf.auth.filter; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 功能說明:【】 * 作 者:lihaijun * 創建日期:2020-11-20 * Spring Security對于獲取TOKEN的請求(默認是"/oauth/token"),需要認證client_id和client_secret。 * 認證client_id和client_secret可以有2種方式,一種是通過本節講的ClientCredentialsTokenEndpointFilter, * 另一種是通過BasicAuthenticationFilter。 * ClientCredentialsTokenEndpointFilter首先比對請求URL是否是TOKEN請求路徑以及請求參數中是否包含 * client_id,如果滿足以上條件,再調用ProviderManager認證client_id和client_secret是否與配置的一致。 * 如果通過認證,會把身份認證信息保存打SecurityContext上下文中。 */ public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter { private AuthorizationServerSecurityConfigurer configurer; private AuthenticationEntryPoint authenticationEntryPoint; public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer, String path) { this.configurer = configurer; setFilterProcessesUrl(path); } @Override public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) { // 把父類的干掉 super.setAuthenticationEntryPoint(null); this.authenticationEntryPoint = authenticationEntryPoint; } @Override protected AuthenticationManager getAuthenticationManager() { return configurer.and().getSharedObject(AuthenticationManager.class); } @Override public void afterPropertiesSet() { setAuthenticationFailureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { authenticationEntryPoint.commence(httpServletRequest, httpServletResponse, e); } }); setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { // 無操作-僅允許過濾器鏈繼續到令牌端點 } }); } } ~~~ ***** **2.2 OAuth2服務配置 【將攔截器放入到攔截鏈條中】** ~~~ package com.hjf.auth.configurer; /** * 功能說明:【授權服務配置】 * 作 者:lihaijun * 創建日期:2020-11-09 */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter { @Autowired private TokenService tokenService; @Autowired private AuthenticationEntryPoint authenticationEntryPoint; @Autowired private CustomUserDetailsService userDetailsService; @Autowired private AuthorizationServerEndpointsConfiguration configuration; @Resource private DataSource dataSource; @Resource private AuthenticationManager authenticationManager; /** * 配置客戶端詳情 */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetails()); } /** * 配置令牌端點得訪問約束 */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { String path = "/oauth/token"; try { // 獲取自定義映射路徑,比如 ((AuthorizationServerEndpointsConfigurer) endpoints).pathMapping("/oauth/token", "/my/token"); path = configuration.oauth2EndpointHandlerMapping().getServletPath(path); } catch (Exception e) { } CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security, path); endpointFilter.afterPropertiesSet(); endpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint); security.authenticationEntryPoint(authenticationEntryPoint); security.addTokenEndpointAuthenticationFilter(endpointFilter); security.tokenKeyAccess("isAuthenticated()") .checkTokenAccess("permitAll()"); } /** * 配置令牌訪問端點、令牌服務 */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenService.tokenStore()); List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getAuthorizationCodeServices(), endpoints.getTokenStore(), endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()); endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters)); endpoints.tokenEnhancer(tokenService.tokenEnhancer()); } /** * 授權模式【用戶名密碼、Token刷新、授權碼、手機號密碼、手機號驗證碼】 * @param authorizationCodeServices * @param tokenStore * @param tokenServices * @param clientDetailsService * @param requestFactory */ private List<TokenGranter> getTokenGranters(AuthorizationCodeServices authorizationCodeServices, TokenStore tokenStore, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { return new ArrayList<>(Arrays.asList( new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory), new CustomRefreshTokenGranter(tokenStore, tokenServices, clientDetailsService, requestFactory), new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory), new MobilePasswordCustomTokenGranter(userDetailsService, tokenServices, clientDetailsService, requestFactory), new MobileSmsCustomTokenGranter(userDetailsService, tokenServices, clientDetailsService, requestFactory) )); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public ClientDetailsService clientDetails() { return new JdbcClientDetailsService(dataSource); } } ~~~ ***** **2.3web安全配置** ~~~ package com.hjf.auth.configurer; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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; /** * 功能說明:【安全配置】 * 作 者:lihaijun * 創建日期:2020-11-09 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() .antMatchers("/oauth/**").permitAll() .anyRequest().permitAll() .and() .csrf().disable(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ~~~ **2.4 自定義令牌授予者【手機號+驗證碼等】** ~~~ package com.hjf.auth.granter; import com.hjf.auth.model.MyUserDetails; import com.hjf.auth.service.CustomUserDetailsService; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import java.util.Map; /** * 功能說明:【自定義手機號+驗證碼令牌授予者】 * 作 者:lihaijun * 創建日期:2020-11-09 */ public class MobileSmsCustomTokenGranter extends AbstractCustomTokenGranter { protected CustomUserDetailsService userDetailsService; public MobileSmsCustomTokenGranter(CustomUserDetailsService userDetailsService, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { super(tokenServices, clientDetailsService, requestFactory, "sms"); this.userDetailsService = userDetailsService; } @Override protected MyUserDetails getCustomUser(Map<String, String> parameters) { String mobile = parameters.get("mobile"); String smscode = parameters.get("smscode"); return userDetailsService.loadUserByMobileAndSmsCode(mobile, smscode); } } ~~~ **2.5 用戶細節服務實現 【具體實現根據認證類型進行用戶數據查詢操作】** ~~~ package com.hjf.auth.service; import com.hjf.api.entity.Account; import com.hjf.auth.constant.CacheKeys; import com.hjf.auth.constant.MessageConstant; import com.hjf.auth.mapper.AccountMapper; import com.hjf.auth.model.MyUserDetails; import com.hjf.core.cache.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.annotation.Resource; /** * 功能說明:【用戶細節服務實現】 * 作 者:lihaijun * 創建日期:2020-11-20 */ @Slf4j @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Resource RedisUtil redisUtil; @Resource AccountMapper accountMapper; /** * 手機號密碼模式驗證邏輯 */ public MyUserDetails loadUserByMobileAndPassword(String mobile, String password) { if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) { throw new InvalidGrantException("您輸入的手機號或密碼不正確"); } Account account=accountMapper.getByTelephone(mobile); if (account==null){ throw new UsernameNotFoundException(MessageConstant.LOGIN_ERROR_ACCOUNT_NOTFOUND); } MyUserDetails userDetails=new MyUserDetails(account); return userDetails; } /** * 手機號和驗證碼驗證邏輯 */ public MyUserDetails loadUserByMobileAndSmsCode(String mobile, String smsCode) { if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(smsCode)) { log.error("【根據手機號和驗證碼查詢用戶信息】 缺失手機號或者驗證碼"); throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE); } Object smsCodeCache=redisUtil.get(CacheKeys.SMS_CODE+mobile); if (smsCodeCache==null){ log.error("【根據手機號和驗證碼查詢用戶信息】 沒有查詢到緩存數據"); throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE); } if (!smsCode.equals(smsCodeCache.toString())){ log.error("【根據手機號和驗證碼查詢用戶信息】 緩存查詢到的信息和輸入信息不匹配"); throw new InvalidGrantException(MessageConstant.LOGIN_ERROR_PHONE_OR_SMSCODE); } Account account=accountMapper.getByTelephone(mobile); if (account==null){ log.error("【根據手機號和驗證碼查詢用戶信息】 數據庫沒有查詢到賬戶信息"); throw new UsernameNotFoundException(MessageConstant.LOGIN_ERROR_ACCOUNT_NOTFOUND); } MyUserDetails userDetails=new MyUserDetails(account); return userDetails; } /** * 用戶名&密碼驗證邏輯【系統ResourceOwnerPasswordTokenGranter使用的默認功能 】 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { String[] permissionArray = new String[]{}; Account account=accountMapper.getByTelephone(username); if (account==null){ log.error("【用戶名&密碼認證】"); throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR); } MyUserDetails securityUser = new MyUserDetails(account); securityUser.setPassword(passwordEncoder.encode(account.getPassword())); securityUser.setEnabled(true); return securityUser; } } ~~~ **2.6 token服務工具【tokenEnhancer、tokenStore、jwtAccessTokenConverter】** ~~~ package com.hjf.auth.token; import com.hjf.auth.model.MyUserDetails; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cglib.beans.BeanMap; import org.springframework.context.annotation.Bean; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import org.springframework.security.rsa.crypto.KeyStoreKeyFactory; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.security.KeyPair; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; /** * 功能說明:【Token服務】 * 作 者:lihaijun * 創建日期:2020-11-20 */ @Component public class TokenService { @Autowired private RedisConnectionFactory redisFactory; private TokenStoreType tokenStoreType = TokenStoreType.JWT; private enum TokenStoreType { IN_MEMORY, REDIS, JWT } @Bean public TokenStore tokenStore() { switch (tokenStoreType) { case REDIS: return new RedisTokenStore(redisFactory); case JWT: return new JwtTokenStore(jwtAccessTokenConverter()); } return new InMemoryTokenStore(); } @Bean public TokenEnhancer tokenEnhancer() { return new TokenEnhancer() { @Override public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) { DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) oAuth2AccessToken; MyUserDetails user = (MyUserDetails) oAuth2Authentication.getPrincipal(); Map<String, Object> map = new LinkedHashMap<>(); map.put("username", user.getUsername()); map.put("telephone", user.getTelephone()); map.put("accountId", user.getAccountId()); token.setAdditionalInformation(map); if (tokenStoreType == TokenStoreType.JWT) { token = (DefaultOAuth2AccessToken) jwtAccessTokenConverter().enhance(oAuth2AccessToken, oAuth2Authentication); } else { token.setValue(buildTokenValue()); if (token.getRefreshToken() != null) { if (token.getRefreshToken() instanceof DefaultExpiringOAuth2RefreshToken) { DefaultExpiringOAuth2RefreshToken refreshToken = (DefaultExpiringOAuth2RefreshToken) token.getRefreshToken(); token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken(buildTokenValue(), refreshToken.getExpiration())); } else { token.setRefreshToken(new DefaultOAuth2RefreshToken(buildTokenValue())); } } } return token; } }; } @Bean public KeyPair keyPair() { //從classpath下的證書中獲取秘鑰對 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); // accessTokenConverter.setSigningKey("demo-oauth2"); ((DefaultAccessTokenConverter) accessTokenConverter.getAccessTokenConverter()).setUserTokenConverter(new DefaultUserAuthenticationConverter() { @Override public Authentication extractAuthentication(Map<String, ?> map) { MyUserDetails customUser = new MyUserDetails(); BeanMap.create(customUser).putAll(map); Object authorities = map.get("authorities"); if (authorities instanceof String) { customUser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities)); } else if (authorities instanceof Collection) { customUser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.collectionToCommaDelimitedString((Collection) authorities))); } return new PreAuthenticatedAuthenticationToken(customUser, null, customUser.getAuthorities()); } }); accessTokenConverter.setKeyPair(keyPair()); return accessTokenConverter; } public String buildTokenValue() { String tokenValue = UUID.randomUUID().toString() + UUID.randomUUID().toString(); return tokenValue; } } ~~~ **2.7 token生成入口** ~~~ package com.hjf.auth.controller; import com.hjf.auth.token.Oauth2TokenDto; import com.hjf.frame.base.BaseResp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.*; import java.security.Principal; import java.util.Map; /** * 功能說明:【認證服務入口】 * 作 者:lihaijun * 創建日期:2020-11-20 */ @RestController @RequestMapping("/oauth") public class OauthController { @Autowired private TokenEndpoint tokenEndpoint; @GetMapping("/token") public BaseResp getAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { return resp(tokenEndpoint.getAccessToken(principal, parameters).getBody()); } @PostMapping("/token") public BaseResp postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { return resp(tokenEndpoint.postAccessToken(principal, parameters).getBody()); } /** * 定制申請返回實體 */ private BaseResp resp(OAuth2AccessToken accessToken) { Oauth2TokenDto oauth2TokenDto = Oauth2TokenDto.builder() .token(accessToken.getValue()) .refreshToken(accessToken.getRefreshToken().getValue()) .expiresIn(accessToken.getExpiresIn()) .tokenHead("Bearer ") .additionalInformation(accessToken.getAdditionalInformation()) .build(); return BaseResp.success(oauth2TokenDto); } } ~~~ ***** 測試 localhost:8000/oauth/token?client\_id=client-mp&client\_secret=123456&mobile=15010123237&password=123456&smscode=1111&grant\_type=sms ![](https://img.kancloud.cn/9c/45/9c45e725c73882e3149bdd6904f90e9a_735x523.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看