springboot oauth 鑒權之——授權碼authorization_code鑒權


 近期一直在研究鑒權方面的各種案例,這幾天有空,寫一波總結及經驗。

第一步:什么是 OAuth鑒權

       OAuth2是工業標准的授權協議。OAuth2取代了在2006創建的原始OAuthTM協議所做的工作。OAuth 2.0關注客戶端開發人員的簡單性,同時為Web應用程序、桌面應用程序、移動電話和客廳設備提供特定的授權流。

     參考理解:  Oauth2.0     理解OAuth 2.0     QQ授權      微信授權

第二步:什么是授權碼模式

        授權碼模式(authorization code)是功能最完整、流程最嚴密的授權模式。它的特點就是通過客戶端的后台服務器,與"服務提供商"的認證服務器進行互動。

第三步:授權碼授權流程

        

交互步驟: 
(A)用戶訪問客戶端,后者將前者導向認證服務器。 (B)用戶選擇是否給予客戶端授權。 (C)假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。 (D)客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后台的服務器上完成的,對用戶不可見。 (E)認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。

第四步:授權碼模式實踐

       源碼地址:

               github: https://github.com/GitHubZhangCom/spring-security-oauth-example/

               碼雲:https://gitee.com/region/spring-security-oauth-example/

 1)、  引入相關jar: 

<!--security  -->
<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
       <groupId>org.springframework.security.oauth.boot</groupId>
       <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.0.0.RELEASE</version>
</dependency>
<!-- 使用lombok優雅的編碼 -->
<dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
</dependency>

 2)、相關oauth編程: 

 

package com.auth.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.core.userdetails.UserDetailsService;

/**
 * 鑒權配置
 * @author zyl
 *
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Qualifier("myUserDetailsService")
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**/*.js",
                        "/**/*.css"
                )
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                // 自動登錄
                /*.and()
                    .rememberMe()
                    // 加密的秘鑰
                    .key("unique-and-secret")
                    // 存放在瀏覽器端cookie的key
                    .rememberMeCookieName("remember-me-cookie-name")
                    // token失效的時間,單位為秒
                    .tokenValiditySeconds(60 * 60 * 25)*/
                .and()
                // 暫時禁用CSRF,否則無法提交登錄表單
                .csrf().disable();

    }

}
package com.auth.server.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

/**
 * 授權配置
 * @author wb0024
 *
 */
@Configuration
@EnableAuthorizationServer
public class ServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;


    @Qualifier("myUserDetailsService")
    @Autowired
    private UserDetailsService userDetailsService;

//    @Autowired
//    @Qualifier("dataSource")
//    private DataSource dataSource;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 配置token獲取和驗證時的策略
        security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client")
                // secret密碼配置從 Spring Security 5.0開始必須以 {加密方式}+加密后的密碼 這種格式填寫
                
                /* *   當前版本5新增支持加密方式:
                 *   bcrypt - BCryptPasswordEncoder (Also used for encoding)
                 *   ldap - LdapShaPasswordEncoder
                 *   MD4 - Md4PasswordEncoder
                 *   MD5 - new MessageDigestPasswordEncoder("MD5")
                 *   noop - NoOpPasswordEncoder
                 *   pbkdf2 - Pbkdf2PasswordEncoder
                 *   scrypt - SCryptPasswordEncoder
                 *   SHA-1 - new MessageDigestPasswordEncoder("SHA-1")
                 *   SHA-256 - new MessageDigestPasswordEncoder("SHA-256")
                 *   sha256 - StandardPasswordEncoder*/
                 
                .secret("{noop}secret")
                .scopes("all")
                .authorizedGrantTypes("authorization_code")//授權碼模式
//                .authorizedGrantTypes("authorization_code", "refresh_token")//授權碼模式
                .autoApprove(true);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        // 配置tokenStore,保存到redis緩存中
//        endpoints.authenticationManager(authenticationManager)
//                .tokenStore(new MyRedisTokenStore(redisConnectionFactory))
//                // 不添加userDetailsService,刷新access_token時會報錯
//                .userDetailsService(userDetailsService);

        // 使用最基本的InMemoryTokenStore生成token
        endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());

    }

    // 使用最基本的InMemoryTokenStore生成token
    @Bean
    public TokenStore memoryTokenStore() {
        return new InMemoryTokenStore();
    }
}

 

 

package com.jwt.server.provider;

import java.util.ArrayList;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
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;


/**
 * 自定義身份認證驗證組件
 * @author zyl
 *
 */
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;

    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder){
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 獲取認證的用戶名 & 密碼
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        // 認證邏輯
        UserDetails userDetails = userDetailsService.loadUserByUsername(name);
        if (null != userDetails) {
            if (bCryptPasswordEncoder.matches(password, userDetails.getPassword())) {
                // 這里設置權限和角色
                ArrayList<GrantedAuthority> authorities = new ArrayList<>();
                authorities.add( new GrantedAuthorityImpl("ROLE_ADMIN"));
                authorities.add( new GrantedAuthorityImpl("ROLE_API"));
                authorities.add( new GrantedAuthorityImpl("AUTH_WRITE"));
                // 生成令牌 這里令牌里面存入了:name,password,authorities, 當然你也可以放其他內容
                Authentication auth = new UsernamePasswordAuthenticationToken(name, password, authorities);
                return auth;
            } else {
                throw new BadCredentialsException("密碼錯誤");
            }
        } else {
            throw new UsernameNotFoundException("用戶不存在");
        }
    }

    /**
     * 是否可以提供輸入類型的認證服務
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

 核心代碼需要做的就這些,詳情請看源碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM