最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Security使用單點(diǎn)登錄的權(quán)限功能

 更新時(shí)間:2022年04月03日 10:15:19   作者:songtianer  
本文主要介紹了Spring Security使用單點(diǎn)登錄的權(quán)限功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

背景

在配置中心增加權(quán)限功能

  • 目前配置中心已經(jīng)包含了單點(diǎn)登錄功能,可以通過統(tǒng)一頁面進(jìn)行登錄,登錄完會(huì)將用戶寫入用戶表
  • RBAC的用戶、角色、權(quán)限表CRUD、授權(quán)等都已經(jīng)完成
  • 希望不用用戶再次登錄,就可以使用SpringSecurity的權(quán)限控制

Spring Security

Spring Security最主要的兩個(gè)功能:認(rèn)證和授權(quán)

功能解決的問題Spring Security中主要類
認(rèn)證(Authentication)你是誰AuthenticationManager
授權(quán)(Authorization)你可以做什么AuthorizationManager

實(shí)現(xiàn)

在這先簡(jiǎn)單了解一下Spring Security的架構(gòu)是怎樣的,如何可以認(rèn)證和授權(quán)的

過濾器大家應(yīng)該都了解,這屬于Servlet的范疇,Servlet 過濾器可以動(dòng)態(tài)地?cái)r截請(qǐng)求和響應(yīng),以變換或使用包含在請(qǐng)求或響應(yīng)中的信息

DelegatingFilterProxy是一個(gè)屬于Spring Security的過濾器

通過這個(gè)過濾器,Spring Security就可以從Request中獲取URL來判斷是不是需要認(rèn)證才能訪問,是不是得擁有特定的權(quán)限才能訪問。

已經(jīng)有了單點(diǎn)登錄頁面,Spring Security怎么登錄,不登錄可以拿到權(quán)限嗎

Spring Security官方文檔-授權(quán)架構(gòu)中這樣說,GrantedAuthority(也就是擁有的權(quán)限)被AuthenticationManager寫入Authentication對(duì)象,后而被AuthorizationManager用來做權(quán)限認(rèn)證

The GrantedAuthority objects are inserted into the Authentication object by the AuthenticationManager and are later read by either the AuthorizationManager when making authorization decisions.

為了解決我們的問題,即使我只想用權(quán)限認(rèn)證功能,也得造出一個(gè)Authentication,先看下這個(gè)對(duì)象:

Authentication

Authentication包含三個(gè)字段:

  • principal,代表用戶
  • credentials,用戶密碼
  • authorities,擁有的權(quán)限

有兩個(gè)作用:

  • AuthenticationManager的入?yún)?,僅僅是用來存用戶的信息,準(zhǔn)備去認(rèn)證
  • AuthenticationManager的出參,已經(jīng)認(rèn)證的用戶信息,可以從SecurityContext獲取

SecurityContext和SecurityContextHolder用來存儲(chǔ)Authentication, 通常是用了線程全局變量ThreadLocal, 也就是認(rèn)證完成把Authentication放入SecurityContext,后續(xù)在整個(gè)同線程流程中都可以獲取認(rèn)證信息,也方便了認(rèn)證

繼續(xù)分析

看到這可以得到,要實(shí)現(xiàn)不登錄的權(quán)限認(rèn)證,只需要手動(dòng)造一個(gè)Authentication,然后放入SecurityContext就可以了,先嘗試一下,大概流程是這樣,在每個(gè)請(qǐng)求上

  • 獲取sso登錄的用戶
  • 讀取用戶、角色、權(quán)限寫入Authentication
  • 將Authentication寫入SecurityContext
  • 請(qǐng)求完畢時(shí)將SecurityContext清空,因?yàn)槭荰hreadLocal的,不然可能會(huì)被別的用戶用到
  • 同時(shí)Spring Security的配置中是對(duì)所有的url都允許訪問的

加了一個(gè)過濾器,代碼如下:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@WebFilter( urlPatterns = "/*", filterName = "reqResFilter" )
public class ReqResFilter implements Filter{

	@Autowired
	private SSOUtils ssoUtils;
	@Autowired
	private UserManager userManager;
	@Autowired
	private RoleManager roleManager;

	@Override
	public void init( FilterConfig filterConfig ) throws ServletException{

	}

	@Override
	public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain )
			throws IOException, ServletException{
		setAuthentication(servletRequest);
		filterChain.doFilter( servletRequest, servletResponse );
		clearAuthentication();
	}

	@Override
	public void destroy(){

	}

	private void setAuthentication( ServletRequest request ){

		Map<String, String> data;
		try{
			data = ssoUtils.getLoginData( ( HttpServletRequest )request );
		}
		catch( Exception e ){
			data = new HashMap<>();
			data.put( "name", "visitor" );
		}
		String username = data.get( "name" );
		if( username != null ){
			userManager.findAndInsert( username );
		}
		List<Role> userRole = userManager.findUserRole( username );
		List<Long> roleIds = userRole.stream().map( Role::getId ).collect( Collectors.toList() );
		List<Permission> rolePermission = roleManager.findRolePermission( roleIds );
		List<SimpleGrantedAuthority> authorities = rolePermission.stream().map( one -> new SimpleGrantedAuthority( one.getName() ) ).collect(
				Collectors.toList() );

		UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( username, "", authorities );
		SecurityContextHolder.getContext().setAuthentication( authenticationToken );
	}

	private void clearAuthentication(){

		SecurityContextHolder.clearContext();
	}
}

從日志可以看出,Principal: visitor,當(dāng)訪問未授權(quán)的接口被拒絕了

16:04:07.429 [http-nio-8081-exec-9] DEBUG org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@cc4c6ea0: Principal: visitor; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: CHANGE_USER_ROLE, CHANGE_ROLE_PERMISSION, ROLE_ADD
...
org.springframework.security.access.AccessDeniedException: 不允許訪問

結(jié)論

不登錄是可以使用Spring Security的權(quán)限,從功能上是沒有問題的,但存在一些別的問題

  • 性能問題,每個(gè)請(qǐng)求都需要請(qǐng)求用戶角色權(quán)限數(shù)據(jù)庫,當(dāng)然可以利用緩存優(yōu)化
  • 我們寫的過濾器其實(shí)也是Spring Security做的事,除此之外,它做了更多的事,比如結(jié)合HttpSession, Remember me這些功能

我們可以采取另外一種做法,對(duì)用戶來說只登錄一次就行,我們?nèi)匀皇强梢允謩?dòng)用代碼再去登錄一次Spring Security的

如何手動(dòng)登錄Spring Security

How to login user from java code in Spring Security? 從這篇文章從可以看到,只要通過以下代碼即可

	private void loginInSpringSecurity( String username, String password ){

		UsernamePasswordAuthenticationToken loginToken = new UsernamePasswordAuthenticationToken( username, password );
		Authentication authenticatedUser = authenticationManager.authenticate( loginToken );
		SecurityContextHolder.getContext().setAuthentication( authenticatedUser );
	}

和上面我們直接拿已經(jīng)認(rèn)證過的用戶對(duì)比,這段代碼讓Spring Security來執(zhí)行認(rèn)證步驟,不過需要配置額外的AuthenticationManager和UserDetailsServiceImpl,這兩個(gè)配置只是AuthenticationManager的一種實(shí)現(xiàn),和上面的流程區(qū)別不大,目的就是為了拿到用戶的信息和權(quán)限進(jìn)行認(rèn)證

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserDetailsServiceImpl implements UserDetailsService{

	private static final Logger logger = LoggerFactory.getLogger( UserDetailsServiceImpl.class );

	@Autowired
	private UserManager userManager;

	@Autowired
	private RoleManager roleManager;

	@Override
	public UserDetails loadUserByUsername( String username ) throws UsernameNotFoundException{

		User user = userManager.findByName( username );
		if( user == null ){
			logger.info( "登錄用戶[{}]沒注冊(cè)!", username );
			throw new UsernameNotFoundException( "登錄用戶[" + username + "]沒注冊(cè)!" );
		}
		return new org.springframework.security.core.userdetails.User( user.getUsername(), "", getAuthority( username ) );
	}

	private List<? extends GrantedAuthority> getAuthority( String username ){

		List<Role> userRole = userManager.findUserRole( username );
		List<Long> roleIds = userRole.stream().map( Role::getId ).collect( Collectors.toList() );
		List<Permission> rolePermission = roleManager.findRolePermission( roleIds );
		return rolePermission.stream().map( one -> new SimpleGrantedAuthority( one.getName() ) ).collect( Collectors.toList() );
	}
}
	@Override
	@Bean
	public AuthenticationManager authenticationManagerBean() throws Exception{

		DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
		daoAuthenticationProvider.setUserDetailsService( userDetailsService );
		daoAuthenticationProvider.setPasswordEncoder( NoOpPasswordEncoder.getInstance() );
		return new ProviderManager( daoAuthenticationProvider );
	}

結(jié)論

通過這樣的方式,同樣實(shí)現(xiàn)了權(quán)限認(rèn)證,同時(shí)Spring Security會(huì)將用戶信息和權(quán)限緩存到了Session中,這樣就不用每次去數(shù)據(jù)庫獲取

總結(jié)

可以通過兩種方式來實(shí)現(xiàn)不登錄使用SpringSecurity的權(quán)限功能

  • 手動(dòng)組裝認(rèn)證過的Authentication直接寫到SecurityContext,需要我們自己使用過濾器控制寫入和清除
  • 手動(dòng)組裝未認(rèn)證過的Authentication,并交給Spring Security認(rèn)證,并寫入SecurityContext

Spring Security是如何配置的,因?yàn)橹皇褂脵?quán)限功能,所有允許所有的路徑訪問(我們的單點(diǎn)登錄會(huì)限制接口的訪問)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;



@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure( HttpSecurity http ) throws Exception{

		http
				.cors()
				.and()
				.csrf()
				.disable()
				.sessionManagement()
				.and()
				.authorizeRequests()
				.anyRequest()
				.permitAll()
				.and()
				.exceptionHandling()
				.accessDeniedHandler( new SimpleAccessDeniedHandler() );
	}


	@Override
	@Bean
	public AuthenticationManager authenticationManagerBean() throws Exception{

		DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
		daoAuthenticationProvider.setUserDetailsService( userDetailsService );
		daoAuthenticationProvider.setPasswordEncoder( NoOpPasswordEncoder.getInstance() );
		return new ProviderManager( daoAuthenticationProvider );
	}

	@Bean
	public CorsConfigurationSource corsConfigurationSource(){

		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins( Collections.singletonList( "*" ) );
		configuration.setAllowedMethods( Arrays.asList( "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" ) );
		configuration.setAllowCredentials( true );
		configuration.setAllowedHeaders( Collections.singletonList( "*" ) );
		configuration.setMaxAge( 3600L );
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration( "/**", configuration );
		return source;
	}

}

參考

 到此這篇關(guān)于Spring Security使用單點(diǎn)登錄的權(quán)限功能的文章就介紹到這了,更多相關(guān)Spring Security單點(diǎn)登錄權(quán)限內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java設(shè)計(jì)模式中的原型模式講解

    Java設(shè)計(jì)模式中的原型模式講解

    原型模式是用于創(chuàng)建重復(fù)的對(duì)象,同時(shí)又能保證性能。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式,今天通過本文給大家介紹下Java?原型設(shè)計(jì)模式,感興趣的朋友一起看看吧
    2023-04-04
  • IntelliJ IDEA中顯示和關(guān)閉工具欄與目錄欄的方法

    IntelliJ IDEA中顯示和關(guān)閉工具欄與目錄欄的方法

    今天小編就為大家分享一篇關(guān)于IntelliJ IDEA中顯示和關(guān)閉工具欄與目錄欄的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • mybatis?plus配置自動(dòng)create_time和update_time方式

    mybatis?plus配置自動(dòng)create_time和update_time方式

    在處理數(shù)據(jù)時(shí),注意時(shí)間類型的轉(zhuǎn)換非常重要,不同編程環(huán)境和數(shù)據(jù)庫對(duì)時(shí)間數(shù)據(jù)的處理方式各異,因此在數(shù)據(jù)遷移或日常處理中需謹(jǐn)慎處理時(shí)間格式,個(gè)人經(jīng)驗(yàn)表明,了解常用的時(shí)間轉(zhuǎn)換函數(shù)和方法能有效避免錯(cuò)誤,提高工作效率,希望這些經(jīng)驗(yàn)?zāi)転榇蠹規(guī)韼椭?/div> 2024-09-09
  • Java程序部署到服務(wù)器上,接口請(qǐng)求下載文件失敗/文件為空/文件名不對(duì)的問題

    Java程序部署到服務(wù)器上,接口請(qǐng)求下載文件失敗/文件為空/文件名不對(duì)的問題

    這篇文章主要介紹了Java程序部署到服務(wù)器上,接口請(qǐng)求下載文件失敗/文件為空/文件名不對(duì),本文給大家分享錯(cuò)誤原因及解決方法,需要的朋友可以參考下
    2020-07-07
  • SpringCloud Alibaba使用Seata處理分布式事務(wù)的技巧

    SpringCloud Alibaba使用Seata處理分布式事務(wù)的技巧

    在傳統(tǒng)的單體項(xiàng)目中,我們使用@Transactional注解就能實(shí)現(xiàn)基本的ACID事務(wù)了,隨著微服務(wù)架構(gòu)的引入,需要對(duì)數(shù)據(jù)庫進(jìn)行分庫分表,每個(gè)服務(wù)擁有自己的數(shù)據(jù)庫,這樣傳統(tǒng)的事務(wù)就不起作用了,那么我們?nèi)绾伪WC多個(gè)服務(wù)中數(shù)據(jù)的一致性呢?跟隨小編一起通過本文了解下吧
    2021-06-06
  • AQS核心流程解析cancelAcquire方法

    AQS核心流程解析cancelAcquire方法

    可以清楚的看到在互斥鎖和共享鎖的拿鎖過程中都是有調(diào)用此方法的,而cancelAcquire()方法是寫在finally代碼塊中,并且使用failed標(biāo)志位來控制cancelAcquire()方法的執(zhí)行
    2023-04-04
  • java8、jdk8日期轉(zhuǎn)化成字符串詳解

    java8、jdk8日期轉(zhuǎn)化成字符串詳解

    在本篇文章中小編給大家整理了關(guān)于java8、jdk8日期轉(zhuǎn)化成字符串的相關(guān)知識(shí)點(diǎn)和代碼,需要的朋友們學(xué)習(xí)下。
    2019-04-04
  • Java 中如何使用 JavaFx 庫標(biāo)注文本顏色

    Java 中如何使用 JavaFx 庫標(biāo)注文本顏色

    這篇文章主要介紹了在 Java 中用 JavaFx 庫標(biāo)注文本顏色,在本文中,我們將了解如何更改標(biāo)簽的文本顏色,并且我們還將看到一個(gè)必要的示例和適當(dāng)?shù)慕忉?,以便更容易理解該主題,需要的朋友可以參考下
    2023-05-05
  • Java分布式鎖、分布式ID和分布式事務(wù)的實(shí)現(xiàn)方案

    Java分布式鎖、分布式ID和分布式事務(wù)的實(shí)現(xiàn)方案

    在分布式系統(tǒng)中,分布式鎖、分布式ID和分布式事務(wù)是常用的組件,用于解決并發(fā)控制、唯一標(biāo)識(shí)和數(shù)據(jù)一致性的問題,本文將介紹Java中常用的分布式鎖、分布式ID和分布式事務(wù)的實(shí)現(xiàn)方案,并通過具體的示例代碼演示它們的用法和應(yīng)用場(chǎng)景
    2023-06-06
  • 關(guān)于jar包增量更新分析

    關(guān)于jar包增量更新分析

    這篇文章主要介紹了關(guān)于jar包增量更新分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評(píng)論

平舆县| 临邑县| 方山县| 遂川县| 元氏县| 屯昌县| 灵宝市| 英超| 海兴县| 准格尔旗| 海丰县| 扶绥县| 苏州市| 广汉市| 伊吾县| 承德市| 江山市| 曲阜市| 鄂托克前旗| 阿克| 高碑店市| 获嘉县| 石嘴山市| 库尔勒市| 周口市| 新平| 孟连| 河北省| 基隆市| 高雄县| 昭苏县| 繁峙县| 崇文区| 白河县| 甘泉县| 太原市| 望谟县| 潮安县| 新竹市| 平江县| 云安县|