Security 、 shiro 和oauth2三方認(rèn)證登錄demo

security 入門教程推薦(此教程寫的很基礎(chǔ),解釋也比較詳細(xì),security入門優(yōu)質(zhì)教程)
社區(qū) Spring Security 從入門到進(jìn)階系列教程

項(xiàng)目地址:https://github.com/athc/hippo

spring security

spring boot 整合 security

org.springframework.security.core.userdetails.User
org.springframework.security.core.userdetails.UserDetails   

1 . 實(shí)現(xiàn)UserDetailsService的loadUserByUsername方法,作用是從數(shù)據(jù)庫獲取用戶信息

//給自定義認(rèn)證方式添加加密方式,在userDetailsService將密碼交給security去驗(yàn)證,在認(rèn)證管理中配置密碼驗(yàn)證方式
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  return new User(userInfo.getAccount(), userInfo.getPassword(), roles);
}

2 . 實(shí)現(xiàn)AuthenticationProvider的authenticate方法根據(jù)UserDetails實(shí)現(xiàn)類獲取用戶信息進(jìn)行用戶密碼,狀態(tài)等相關(guān)驗(yàn)證

3 . 告訴security認(rèn)證方式

  /**
 * 添加自定義登錄到認(rèn)證security管理
 * 
 */
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
  //用戶認(rèn)證邏輯,這里可以只配置userAuthenticationProvider 剩下的邏輯判斷在provider中完成,如果配置了userDetailsService會(huì)去驗(yàn)證密碼
  auth.authenticationProvider(userAuthenticationProvider)
          //獲取用戶信息
     // .userDetailsService(userDetailsService) 
      //密碼加密方式
      //.passwordEncoder(passwordEncoder());
}

4 . 訪問資源控制,http.authorizeRequests()方法有多個(gè)子節(jié)點(diǎn),每個(gè)macher按照他們的聲明順序執(zhí)行,路徑配置順序有要求 ,匹配就返回.
hasAnyAuthority("USER")需要有USER權(quán)限才能訪問;
hasAnyRole("ADMIN")會(huì)自動(dòng)給ADMIN加上ROLE_前綴,需要有ROLE_ADMIN角色才能訪問。

/**
     * security 攔截路徑
     * http.authorizeRequests()方法有多個(gè)子節(jié)點(diǎn),每個(gè)macher按照他們的聲明順序執(zhí)行
     * 路徑配置順序有要求 ,匹配就返回
     *
     * @param http
     * @throws Exception
     */
    @Override protected void configure(HttpSecurity http) throws Exception {
      http.csrf().disable()
          .authorizeRequests()
          .antMatchers("/security/login/**").permitAll()
          .antMatchers("/security/user/**").hasAnyAuthority("USER")
          .antMatchers("/security/role/**").hasAnyRole("ADMIN")
          .anyRequest().authenticated()
          .and()
          .rememberMe()
          .key("my-secret")
          .rememberMeCookieName("my-cookie-name")
          .tokenValiditySeconds(24 * 60 * 60)
          .and()
          .formLogin()
          .and()
          .logout()
          .and()
          .httpBasic()
      ;
      // 在 UsernamePasswordAuthenticationFilter 前添加自定義過濾器 BeforeLoginFilter 
      http.addFilterBefore(new BeforeLoginFilter(), UsernamePasswordAuthenticationFilter.class);
    }

整合oauth2

項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth2-security

security-oauth2區(qū)分了客戶端和用戶。

5 . 實(shí)現(xiàn)ClientDetailsService的loadClientByClientId方法,實(shí)現(xiàn)客戶端認(rèn)證

6 . 配置認(rèn)證server(@EnableAuthorizationServer)通過繼承AuthorizationServerConfigurerAdapter配置認(rèn)證oauth2自定義客戶端和用戶認(rèn)證

//client認(rèn)證
@Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.withClientDetails(clientDetailsService);
  }

  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
            //token存儲(chǔ)位置
        .tokenStore(new InMemoryTokenStore())
        //將web security配置的authenticationManager
        .authenticationManager(authenticationManager)
        //刷新token會(huì)用到userDetailsService
        .userDetailsService(userDetailsService)
        .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
  }

  @Override
  public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    //允許驗(yàn)證token 接口訪問,單點(diǎn)登錄會(huì)訪問這個(gè)接口驗(yàn)證token是否有效
    oauthServer.checkTokenAccess("permitAll()");
    //加密方式
    oauthServer.passwordEncoder(passwordEncoder());
    //允許表單認(rèn)證
    oauthServer.allowFormAuthenticationForClients();
  }

7 . 修改security的資源控制,不攔截oauth2資源

@Override protected void configure(HttpSecurity http) throws Exception {
      http
          .authorizeRequests()
          .antMatchers("/oauth/*").permitAll()
          .and().httpBasic()
      ;
    }
    
   /**
      * 在這security中,把AuthenticationManager交給Spring,
      * 這一步的配置是必不可少的,否則SpringBoot會(huì)自動(dòng)配置一個(gè)AuthenticationManager,覆蓋掉內(nèi)存中的用戶
      */
     @Bean
     @Override
     public AuthenticationManager authenticationManagerBean() throws Exception {
       AuthenticationManager manager = super.authenticationManagerBean();
       return manager;
     }

8 . 配置資源server(@EnableResourceServer) 繼承ResourceServerConfigurerAdapter配置oauth2資源控制

@Configuration
@EnableResourceServer
class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  @Override
  public void configure(ResourceServerSecurityConfigurer resources) {
    //資源id和loadClientByClientId查詢到的相匹配
    resources.resourceId("API");
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        //必須認(rèn)證過后才可以訪問
        .antMatchers("/security/user/**").hasAnyAuthority("USER")
        .antMatchers("/security/role/**").hasAnyRole("ADMIN")
        .anyRequest().permitAll()
    ;
  }
}
/**
 * oauth2 幾種獲取token方式 client 可用basic 方式傳遞
 * refresh token: http://localhost:8013/oauth/token?grant_type=refresh_token&refresh_token=3680e51e-fbf4-417a-85d9-6a8205c14c0a&client_id=user&client_secret=123456
 * client: http://localhost:8013/oauth/token?client_id=user&client_secret=123456&scope=read&grant_type=client_credentials
 * password: http://localhost:8013/oauth/token?username=zhangsan&password=123456&grant_type=password&scope=read&client_id=user&client_secret=1234567
 * authorization code: http://localhost:8013/oauth/authorize?response_type=code&client_id=code&redirect_uri=http://localhost:8013/security/login&scope=all
 */

整合oauth2 短信認(rèn)證登錄

項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth-third

1 攔截器攔截短信驗(yàn)證登錄

繼承認(rèn)證攔截器AbstractAuthenticationProcessingFilter,重寫認(rèn)證方法
獲取到手機(jī)號(hào)驗(yàn)證碼,組裝認(rèn)證認(rèn)證的Token類,提交認(rèn)證
流程大致為:

1 攔截短信認(rèn)證登錄,提交短信認(rèn)證

2 短信認(rèn)證邏輯處理,認(rèn)證成功,提交認(rèn)證信息

3 生成 token

@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
 //短信驗(yàn)證預(yù)處理  
 if (!RequestMethod.POST.name().equals(request.getMethod())) {
     throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
   }
   String mobile = request.getParameter("mobile").trim();
   String code = request.getParameter("code").trim();
   if (mobile.isEmpty() || code.isEmpty()) {
     throw new VerifyException("mobile or code cant be null");
   }
   
   //組裝認(rèn)證參數(shù)
   SmsToken authRequest = new SmsToken(mobile, code, new ArrayList<SimpleGrantedAuthority>());
   setDetails(request, authRequest);
   //提交authenticationManager 認(rèn)證
  
   return this.getAuthenticationManager().authenticate(authRequest);
 }

security 配置短信認(rèn)證 邏輯處理provider

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
  //密碼認(rèn)證
  auth
      .authenticationProvider(userAuthenticationProvider)
      .userDetailsService(userDetailsService)
      //密碼驗(yàn)證方式
      .passwordEncoder(new BCryptPasswordEncoder())
  ;
  //sms認(rèn)證
  auth
      .authenticationProvider(smsProvider)
      .userDetailsService(userDetailsService)
  ;
}

類似 用戶名密碼認(rèn)證的短信認(rèn)證 provider

@Override public Authentication authenticate(Authentication authentication) {
 //短信驗(yàn)證碼邏輯處理
   SmsToken token = (SmsToken) authentication;
   String mobile = (String) token.getPrincipal();
   String code = (String) token.getCredentials();
   UserDetails user = userDetailsService.loadUserByUsername(mobile);
   logger.info(code);
   //fixme: 驗(yàn)證code
   if (code != code) {
     throw new CredentialsExpiredException("$code expired.");
   }
   //返回認(rèn)證完成Token
   return new SmsToken(user, null, user.getAuthorities());
 }

//支持自定義Token
 @Override public boolean supports(Class<?> authentication) {
   return SmsToken.class.isAssignableFrom(authentication);
 }

整合oauth2 三方認(rèn)證登錄

項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth-third

參數(shù)注入到類

@Bean
  @ConfigurationProperties("sina.client")
  public AuthorizationCodeResourceDetails sina() {
    return new AuthorizationCodeResourceDetails();
  }

  @Bean
  @Qualifier("sinaResource")
  @Primary
  @ConfigurationProperties("sina.resource")
  public ResourceServerProperties sinaResource() {
    return new ResourceServerProperties();
  }

三方登錄請(qǐng)求攔截

 private Filter ssoFilter() {
    CompositeFilter filter = new CompositeFilter();
    List<Filter> filters = new ArrayList<>();
    OAuth2ClientAuthenticationProcessingFilter sinaFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/sina");
    OAuth2RestTemplate sinaTemplate = new OAuth2RestTemplate(sina(), oauth2ClientContext);
    sinaFilter.setRestTemplate(sinaTemplate);
    //自定義userInfo 獲取
    SinaUserInfoTokenServices sinaTokenServices = new SinaUserInfoTokenServices(sinaResource().getUserInfoUri(), sina().getClientId());
    sinaTokenServices.setRestTemplate(sinaTemplate);
    sinaFilter.setTokenServices(sinaTokenServices);
    //認(rèn)證成功處理
    sinaFilter.setAuthenticationSuccessHandler(authSuccessHandler);
    //獲取到三方信息 自定義處理 存庫等
    sinaTokenServices.setAuthoritiesExtractor(new MyAuthoritiesExtractor());
    //三方登錄權(quán)限處理
    sinaTokenServices.setPrincipalExtractor(new MyPrincipalExtractor());
    filters.add(sinaFilter);
    filter.setFilters(filters);
    return filter;
  }

將攔截器放到攔截鏈中

自定義token:

實(shí)現(xiàn)自定義token產(chǎn)生
public class UserTokenEnhancer implements TokenEnhancer {
  @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    DefaultOAuth2AccessToken result = (DefaultOAuth2AccessToken) accessToken;
    //uuid 去掉   `-`
    result.setValue(result.getValue().replace("-", ""));
    result.setRefreshToken(new DefaultOAuth2RefreshToken(UUID.randomUUID().toString().replace("-", "")));
    //todo: 這里可以自定義token數(shù)據(jù)結(jié)構(gòu)
    return result;
  }
}

從獲取的三方信息中 獲取有用的信息

MyPrincipalExtractor implements PrincipalExtractor

三方認(rèn)證登錄獲取的權(quán)限

MyAuthoritiesExtractor implements AuthoritiesExtractor

security+oauth2+sso demo地址:https://github.com/athc/ath-cloud -----用的kotlin寫的單點(diǎn)登錄demo

參考鏈接:
https://spring.io/guides/tutorials/spring-boot-oauth2/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容