Spring Security集成Oauth2實現用戶身份驗證+授權

前言

最近的項目有使用到Sping Security和Oauth做用戶身份驗證和授權機制,所以在網上找了點資料簡單學習並寫了個小demo,在此進行記錄總結。授權這塊是直接拷貝了參考鏈接中的小demo,後續涉及到第三方應用授權碼方式授權的話再學習然後總結

參考鏈接

1. Spring Security瞭解:https://www.springcloud.cc/spring-security-zhcn.html

2. Spring Boot集成Spring Security:

https://www.jianshu.com/p/afe6619d9663

https://www.cnblogs.com/lenve/p/11242055.html

https://blog.csdn.net/qq_29580525/article/details/79317969

https://blog.csdn.net/yuanlaijike/article/details/80249235

https://blog.csdn.net/yuanlaijike/article/details/84703690

3. Oauth2瞭解:

http://www.ruanyifeng.com/blog/2019/04/oauth_design.html

http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

https://www.cnblogs.com/meibaorui/p/9182660.html

4. Spring Security集成Oauth2:

https://www.cnblogs.com/fernfei/p/12200226.html

https://blog.csdn.net/weixin_34080571/article/details/91715402

https://www.cnblogs.com/dalianpai/p/12425962.html

實現過程

1. Spring Boot集成Spring Security

1.1 導入依賴以及配置服務器相關屬性

1.1.1 導入依賴

可直接建立Spring Boot項目選擇Spring Security,或者也可以建立一個空的Spring Boot項目後在pom.xml依賴配置文件中加入相關依賴。

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

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

1.1.2 配置服務器相關屬性

一般配置服務器的訪問端口和初始路徑,默認爲8080以及/,配置在resources/application.properties中配置

#配置服務器端口號
server.port=80
#配置服務器入口網址
server.servlet.context-path=/spring

1.2 普通web訪問實現(UserController)

這裏是寫了個登錄、home頁面,但是由於沒有寫相應的前端頁面,所以當用戶跳轉/login頁面時會顯示404錯誤,以及用戶驗證成功後跳轉的home頁面也會顯示404錯誤。此處說明一下實際中會根據需要在SecurityConfig中對頁面是否攔截以及跳轉情況進行統一規範設計,此demo中直接使用Spring Security默認的login表單,用戶身份信息驗證成功後會將信息直接打印輸出到前端。

@RestController()
@RequestMapping("/user")
public class UserController {

    @PostMapping("login")
    public String login(){
        return "login.html";
    }

    //由於沒有寫對應的靜態頁面,所以用戶身份驗證成功後訪問這些頁面會報404錯誤;
    // 如果身份未驗證成功會跳轉login頁面(自己手動寫的/Spring Security默認的login表單)
    @GetMapping("home")
    public String home(){
        return "home.html";
    }

    /**
     * 返回當前登錄的用戶信息(存在SecurityContextHolder的全局變量中)
     * @return
     */
    @GetMapping("/whoim")
    //@PreAuthorize("hasRole('ROLE_USER')") //@PreAuthorize 用於判斷用戶是否有指定權限,沒有就不能訪問
    public Object whoIm(){
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

1.3 集成Spring Security(SecurityConfig)使用Java內存用戶名和密碼(指定可登錄/通過攔截的用戶名和密碼,其中密碼爲明文)

SecurityConfig類繼承WebSecurityConfigurerAdapter類,主要實現幾個config方法,用來進行接口請求攔截/用戶身份驗證/前端靜態頁面攔截

/**
 * 實現一個WebSecurityConfigurerAdapter然後重寫一下configure方法,配置登錄驗證頁、請求權限設置
 * 標識該類是配置類、開啓 Security 服務、開啓全局 Securtiy 註解
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //啓用基於註解的安全性 prePostEnable表示使用基於表達書的語法
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    ObjectMapper objectMapper; //json轉換

    /**
     * 接口請求攔截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //安全請求策略,方法可有多個子節點matcher,每個matcher按照其聲明順序執行
                //頁面訪問權限控制說明
                .antMatchers("/").permitAll() //允許任何用戶訪問/路徑(這個路徑得有頁面,否則後報404錯誤)
                .antMatchers("/admin/**").hasRole("ADMIN") //以/admin/開頭的URL只能由擁有ROLE_ADMIN角色的用戶訪問
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //同理以/db/開頭的URL
                // 只能由同時具有ROLE_ADMIN和ROLE_DBA的用戶訪問
                .anyRequest()
//                .access("權限認證bean") ////必須經過權限認證以後才能訪問
                .authenticated() //其它請求進行攔截(需要進行認證)
                .and()
            .formLogin() //支持基於表單的登錄驗證
//                .loginPage("../../resources/static/login") //指定登錄的路徑(用戶需要登錄驗證時跳轉login頁面)
//                .loginPage("/user/login") //指定跳轉controller層的相關方法中
//                .loginProcessingUrl("/index")  //登錄成功後跳轉/index頁面(攔截通過後)
                .permitAll() //允許基於表單登錄的所有的URL的所有用戶的訪問
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
//                .and() //and,相當於XML標籤的關閉,允許繼續配置父類節點
//            .rememberMe() //實現rememberMe功能(短時間內免登錄)
//                .rememberMeParameter("remember me").userDetailsService(userDetailsService) //remember me爲參數
//                .tokenRepository(persistenceTokenRepository) //定義好的token存儲類
//                .tokenValiditySeconds(60) //token有效時間爲60s
                .and()
            .logout() //提供註銷支持
                .logoutUrl("/logout") //註銷時觸發跳轉/logout頁面
                .logoutSuccessUrl("/index") //註銷成功後跳轉/index頁面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名內部類
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定後/index會被忽略,註銷完成執行logoutSuccessHandler,可拋出異常
                .invalidateHttpSession(true) //註銷時讓HttpSession無效
//                .addLogoutHandler(logoutHandler) //被用來執行必要的清理,不可拋出異常
//                .deleteCookies(cookieNamesToClear) //註銷成功後移除命名爲cookieNameToClear的cookie
                .permitAll() //允許任何用戶訪問
                .and()
//            .httpBasic() //允許用戶使用基於HTTP驗證進行認證 (Spring Security支持兩種認證方式:HTTP和Form表單)
//                .and()
            .csrf().disable(); //禁用CSRF保護
        http.cors().disable();
    }

    /**
     * 設置通過請求攔截(登錄驗證)放行後/登錄成功後的處理
     * 此處也可直接寫在configure(HttpSecurity http)當做匿名內部類寫法/Lambda表達式實現
     */
    @Component("userAuthenticationSuccessHandler")
    public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationSuccessHandler.class);

        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            logger.info("登錄成功");
            //設置返回Response的類型和值
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            //轉爲json格式返回到前端
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
            System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());

            //跳轉whoim頁面
//            new DefaultRedirectStrategy().sendRedirect(httpServletRequest,httpServletResponse,"/whoim");
        }
    }

    /**
     * 設置通過請求攔截(登錄驗證)攔截成功後/登錄失敗後的處理
     */
    @Component("userAuthenticationFailureHandler")
    public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationFailureHandler.class);

        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            logger.info("登錄失敗");
            httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
        }
    }

    /**
     * 配置一個正常的驗證
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置兩個用戶+密碼+角色可驗證成功(Java內存配置用戶名和密碼)
        auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()) //使用明文,不對密碼進行加密操作
                //NoOpPasswordEncoder是一個final類,使用getInstance靜態方法獲取實例(該類中對密碼不做任何加密處理)
                .withUser("user").password("123456").roles("USER")
                .and()
                .withUser("admin").password("admin").roles("USER","ADMIN");
    }

    /**
     * 前端(靜態頁面)攔截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**"); //對靜態js、css、images放行
    }
}

NoOpPasswordEncoder類如下圖:

1.4 改造實現密碼加密(AuthProvider、UserDetailServiceImpl)

實際應用中明文密碼太過於不安全,所以一般會定義加密對象,對密碼進行加密操作。同時實際中用戶名和密碼基本都是有數據庫支撐的,所以基本邏輯是攔截後進行根據用戶輸入的用戶名和密碼 然後拿着用戶名去數據庫中找,如果找到了用戶,那麼進行加密後的密碼匹配操作,如果匹配成功那麼說明用戶身份認證成功,進行成功後的相關處理即可(一般會存儲session保證用戶短時間內的免登錄操作,另外會將用戶權限加入security權限列表,如果系統支持RABC那麼還會與角色相關聯,實現基於角色的用戶權限管理操作)

1.4.1 SecurityConfig

主要改動的地方在於配置用戶身份驗證的configure方法

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //一般是數據庫中獲取用戶名和密碼進行匹配
      auth.authenticationProvider(authProvider).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
/**
 * 實現一個WebSecurityConfigurerAdapter然後重寫一下configure方法,配置登錄驗證頁、請求權限設置
 * 標識該類是配置類、開啓 Security 服務、開啓全局 Securtiy 註解
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //啓用基於註解的安全性 prePostEnable表示使用基於表達書的語法
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    ObjectMapper objectMapper; //json轉換
    @Autowired
    AuthProvider authProvider;
    @Autowired
    UserDetailsServiceImpl userDetailsService;

    /**
     * springboot2.x引入的security版本是5.x的,這個版本需要提供一個PasswordEncoder實例,不然就會報錯
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份認證管理
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * 接口請求攔截
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests() //安全請求策略,方法可有多個子節點matcher,每個matcher按照其聲明順序執行
                //頁面訪問權限控制說明
                .antMatchers("/").permitAll() //允許任何用戶訪問/路徑(這個路徑得有頁面,否則後報404錯誤)
                .antMatchers("/admin/**").hasRole("ADMIN") //以/admin/開頭的URL只能由擁有ROLE_ADMIN角色的用戶訪問
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //同理以/db/開頭的URL
                // 只能由同時具有ROLE_ADMIN和ROLE_DBA的用戶訪問
                .anyRequest()
                .authenticated() //其它請求進行攔截(需要進行認證)
                .and()
            .formLogin() //支持基於表單的登錄驗證
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
                .permitAll() //允許基於表單登錄的所有的URL的所有用戶的訪問
                .and()
            .logout() //提供註銷支持
                .logoutUrl("/logout") //註銷時觸發跳轉/logout頁面
                .logoutSuccessUrl("/index") //註銷成功後跳轉/index頁面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名內部類
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定後/index會被忽略,註銷完成執行logoutSuccessHandler,可拋出異常
                .invalidateHttpSession(true) //註銷時讓HttpSession無效
                .permitAll() //允許任何用戶訪問
                .and()
            .csrf().disable(); //禁用CSRF保護
        http.cors().disable();
    }

    /**
     * 設置通過請求攔截(登錄驗證)放行後/登錄成功後的處理
     * 此處也可直接寫在configure(HttpSecurity http)當做匿名內部類寫法/Lambda表達式實現
     */
    @Component("userAuthenticationSuccessHandler")
    public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationSuccessHandler.class);

        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            logger.info("登錄成功");
            //設置返回Response的類型和值
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            //轉爲json格式返回到前端
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(authentication));
            System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
            //如果需要保存session則在此處進行對request進行session保存操作(如果有多個用戶並且跨域,可考慮將session存儲在redis緩存數據庫中)
            //跳轉whoim頁面
//            new DefaultRedirectStrategy().sendRedirect(httpServletRequest,httpServletResponse,"/whoim");
        }
    }

    /**
     * 設置通過請求攔截(登錄驗證)攔截成功後/登錄失敗後的處理
     */
    @Component("userAuthenticationFailureHandler")
    public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler{
        private Logger logger = LoggerFactory.getLogger(UserAuthenticationFailureHandler.class);

        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
            logger.info("登錄失敗");
            httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(e));
        }
    }

    /**
     * 配置一個正常的驗證
     * 將我們自定義的 userDetailsService 注入進來,
     * 在 configure()方法中使用 auth.userDetailsService()方法替換掉默認的 userDetailsService
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //一般是數據庫中獲取用戶名和密碼進行匹配
        auth.authenticationProvider(authProvider).userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 前端(靜態頁面)攔截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**","/css/**","/images/**"); //對靜態js、css、images放行
    }
}

1.4.2 UserDetailsServiceImpl

該類主要是注入用戶的信息(用戶名和密碼)返回一個User對象

/**
 * 實現UserDetailsService接口進行用戶姓名密碼校驗(將用戶信息和權限注入進來)
 * 返回值是UserDetails,是一個接口,
 * 一般使用其子類org.springframework.security.core.userdetails.User,它有三個參數,分別是用戶名、密碼和權限集
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    PasswordEncoder passwordEncoder;

    //創建一個日誌對象
    private Logger logger = LoggerFactory.getLogger(UserDetailsService.class);

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        logger.info("登錄用戶輸入用戶名:{}",username);
        //根據username查找用戶信息
        //...此處應該爲調用數據庫mapper類的根據用戶名查找用戶操作

        //密碼進行加密
        String pwd = "123456"; //實際應該根據數據庫進行加密
        //對密碼進行brypt加密
        String cyptPwd = passwordEncoder.encode(pwd);
        //一般如果有數據庫的話密碼加密操作在添加用戶的時候已經實現,這裏只需要獲取到用戶的密碼即可

        logger.info("加密後密碼爲:{}",cyptPwd);
        //返回賬號 密碼 權限
        return new User("username",cyptPwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}

1.4.3 AuthProvider

該類實現用戶名和密碼的匹配驗證操作

@Component //勿忘
public class AuthProvider implements AuthenticationProvider {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //獲取用戶名和密碼
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
        //進行密碼比對(解密後的)
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        boolean flag = passwordEncoder.matches(password,userDetails.getPassword());
        if(flag){//驗證通過
            return new UsernamePasswordAuthenticationToken(username,password,userDetails.getAuthorities()); //返回一個token對象,參數爲用戶名、密碼和權限列表
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

1.5 測試

1.5.1 未登錄輸入網址會跳轉login頁面

輸入網址自動跳轉login頁面

1.5.2 登錄後可正常訪問系統的頁面(由於沒寫前端html,所以404)登錄成功根據successHandler將提示信息返回到瀏覽器頁面上。

1.6 註解問題

對於SpringBoot項目中的配置類都需要加@Configuration註解進行註冊,否則會報錯無效

生成的實體方法/類需要加@Bean註解進行註冊

SecurityConfig方法中的註解表示該類是配置類、開啓 Security 服務、開啓全局 Securtiy 註解

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //啓用基於註解的安全性 prePostEnable表示使用基於表達書的語法

1.7 Spring Security簡介

一個Web應用程序會有很多的潛在漏洞,例如跨站腳本、請求僞造和會話劫持。Spring Security是一個強大高可定製的身份驗證和訪問控制框架,是確保基於Spring的應用程序的標準,可儘可能的避免這些潛在漏洞。其底層實現爲一條過濾器鏈,就是用戶請求進來,判斷有沒有請求的權限,拋出異常,重定向跳轉

認證/身份驗證:爲用戶建立一個他所聲明的主體,主體一般是指用戶、設備或者可以在系統中執行動作的其他系統

授權/訪問控制:一個用戶能夠在應用中執行某個操作,在到達授權判斷之前,身份的主體已由身份驗證過程建立。Spring Security提供的授權功能:授權web請求、授權方法是否可被調用、授權訪問單個域對象的實例(先認證再授權)

核心組件:spring-security-core

SecurityContextHolder:提供幾種訪問SecurityContext的方式。存儲當前應用程序的安全環境,包含應用當前使用的主體細節,默認使用ThreadLocal存儲(安全環境在同一個線程執行的方法一直有效),可通過設置系統屬性,或者調用SecurityContextHolder的靜態方法更改默認的SecurityContextHolder.MODE_THREADLCOAL模式。

SecurityContext:保存Authentication信息和請求對應的安全信息

Authentication:存儲目前與應用程序交互的主要細節

GrantedAuthority:在應用程序範圍賦予主體的權限

UserDetails:Spring Security的核心接口,是從Authentication中獲得的安全主體

UserDetailsService:創建一個UserDetails,傳遞一個String類型的用戶名/證書ID(根據SecurityConfig實現一個大的驗證攔截框框/過濾器,然後通過AuthProvider進行身份驗證,其中匹配的對象是通過UserDetailsService返回的一個UserDetails目標用戶驗證對象)

2. Spring Security集成Oauth2

2.1 導入依賴

       <dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
		</dependency>

2.2 Spring Security集成Oauth2

2.2.1 授權服務器配置(AuthorizationServiceConfig)

@Configuration //配置類
@EnableAuthorizationServer //開啓授權服務
public class AuthorizationServiceConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    RedisConnectionFactory redisConnectionFactory; //redis連接池
    @Autowired
    UserDetailsServiceImpl userDetailsService;

    /**
     * // 定義了客戶端應用的通行證
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client_id") //客戶端賬號
                .authorizedGrantTypes("password","refresh_token") //授權類型(支持四種類型:授權碼類型、隱藏式類型、密碼類型、客戶端憑證,以密碼類型爲例)
                .accessTokenValiditySeconds(60*60*24) //授權有效期1天
                .refreshTokenValiditySeconds(60*60*1) //刷新有效期1h
                .resourceIds("rid")
                .scopes("all") //授權作用域
//                .secret("secretKey") //密碼
                //對密碼加密
                .secret(new BCryptPasswordEncoder().encode("secretKey"))
                .redirectUris("http://localhost:80/spring/login"); //驗證回調地址
    }

    /**
     * 身份驗證管理
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //不同的驗證方式此處寫法不同
        //使用密碼驗證方式授權
        endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) //token存儲在redis中
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    /**
     * 驗證方式
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允許用戶表單身份驗證
        security.allowFormAuthenticationForClients();
    }
}

2.2.2 資源服務器配置(ResourceServerConfig)

一般來講授權服務器和資源服務器會分開,這個demo是整合在一臺機子上配置。

@Configuration
@EnableResourceServer //開啓資源服務
public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {

    /**
     * 資源訪問授權配置(哪些資源訪問需要進行授權)
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated();//其餘所有資源訪問都需要進行授權
    }

    /**
     * 可訪問的資源
     * @param resources
     * @throws Exception
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("rid");
    }
}

2.3.3 修改Spring Security配置(SecurityConfig)

添加oauth鏈接的攔截

http.requestMatchers()
                .antMatchers("/oauth/**","/spring/login/**","/logout/**") //放行oauth授權頁面,login頁面,logout頁面
                .and()
                .authorizeRequests() //安全請求策略,方法可有多個子節點matcher,每個matcher按照其聲明順序執行
                .antMatchers("/oauth/**").authenticated()
                .and()
            .formLogin() //支持基於表單的登錄驗證
                .permitAll() //允許基於表單登錄的所有的URL的所有用戶的訪問
                .successHandler(new UserAuthenticationSuccessHandler())
                .failureHandler(new UserAuthenticationFailureHandler())
                .and()
            .logout() //提供註銷支持
                .logoutUrl("/logout") //註銷時觸發跳轉/logout頁面
                .logoutSuccessUrl("/index") //註銷成功後跳轉/index頁面
                .logoutSuccessHandler(new LogoutSuccessHandler(){ //匿名內部類
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=UTF-8");
                        httpServletResponse.getWriter().println("登出成功");
                    }
                }) //指定後/index會被忽略,註銷完成執行logoutSuccessHandler,可拋出異常
                .invalidateHttpSession(true) //註銷時讓HttpSession無效
                .permitAll() //允許任何用戶訪問
                .and()
            .csrf().disable(); //禁用CSRF保護

2.4 Oauth2簡介

OAuth(Open Authorization開放授權)是一個開放標準,允許用戶授權第三方移動應用訪問他們存儲在另外的服務提供者上的信息,而不需要將用戶名和密碼提供給第三方移動應用或分享他們數據的所有內容。

授權模式有四種:授權碼模式、隱藏模式、密碼模式、客戶端模式。其中一般第三方應用都使用的是授權碼模式。

給一個以QQ爲實例的授權碼權限驗證模式的鏈接:https://blog.csdn.net/weixin_43553694/article/details/105223236

簡單來說就是用戶先向授權服務器申請授權碼(申請的過程需要持有資源的用戶手動同意),申請成功後拿着這個授權碼再去服務器申請一個令牌(token);拿到token以後發送token到資源服務器中訪問資源,資源服務器拿到這個token進行驗證,驗證令牌成功則允許用戶訪問資源,否則拒絕用戶訪問。

總結

學習的兩個路徑,其一是沿着學習路線看視頻看教程寫demo進行總結;其二是根據實際項目中遇到的問題和技術點進行研究學習寫demo進行總結。學就完事兒了。

另外這篇博客有些過於基礎了,之後深入學習後繼續進行不斷總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章