<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在設定中心增加許可權功能
Spring Security最主要的兩個功能:認證和授權
功能 | 解決的問題 | Spring Security中主要類 |
---|---|---|
認證(Authentication) | 你是誰 | AuthenticationManager |
授權(Authorization) | 你可以做什麼 | AuthorizationManager |
在這先簡單瞭解一下Spring Security的架構是怎樣的,如何可以認證和授權的
過濾器大家應該都瞭解,這屬於Servlet的範疇,Servlet 過濾器可以動態地攔截請求和響應,以變換或使用包含在請求或響應中的資訊
DelegatingFilterProxy是一個屬於Spring Security的過濾器
通過這個過濾器,Spring Security就可以從Request中獲取URL來判斷是不是需要認證才能存取,是不是得擁有特定的許可權才能存取。
Spring Security官方檔案-授權架構中這樣說,GrantedAuthority(也就是擁有的許可權)被AuthenticationManager寫入Authentication物件,後而被AuthorizationManager用來做許可權認證
The GrantedAuthority objects are inserted into the Authentication object by the AuthenticationManager and are later read by either the AuthorizationManager when making authorization decisions.
為了解決我們的問題,即使我只想用許可權認證功能,也得造出一個Authentication,先看下這個物件:
Authentication包含三個欄位:
有兩個作用:
SecurityContext和SecurityContextHolder用來儲存Authentication, 通常是用了執行緒全域性變數ThreadLocal, 也就是認證完成把Authentication放入SecurityContext,後續在整個同執行緒流程中都可以獲取認證資訊,也方便了認證
看到這可以得到,要實現不登入的許可權認證,只需要手動造一個Authentication,然後放入SecurityContext就可以了,先嚐試一下,大概流程是這樣,在每個請求上
加了一個過濾器,程式碼如下:
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,當存取未授權的介面被拒絕了
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: 不允許存取
不登入是可以使用Spring Security的許可權,從功能上是沒有問題的,但存在一些別的問題
我們可以採取另外一種做法,對使用者來說只登入一次就行,我們仍然是可以手動用程式碼再去登入一次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 ); }
和上面我們直接拿已經認證過的使用者對比,這段程式碼讓Spring Security來執行認證步驟,不過需要設定額外的AuthenticationManager和UserDetailsServiceImpl,這兩個設定只是AuthenticationManager的一種實現,和上面的流程區別不大,目的就是為了拿到使用者的資訊和許可權進行認證
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( "登入使用者[{}]沒註冊!", username ); throw new UsernameNotFoundException( "登入使用者[" + username + "]沒註冊!" ); } 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 ); }
通過這樣的方式,同樣實現了許可權認證,同時Spring Security會將使用者資訊和許可權快取到了Session中,這樣就不用每次去資料庫獲取
可以通過兩種方式來實現不登入使用SpringSecurity的許可權功能
Spring Security是如何設定的,因為只使用許可權功能,所有允許所有的路徑存取(我們的單點登入會限制介面的存取)
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; } }
到此這篇關於Spring Security使用單點登入的許可權功能的文章就介紹到這了,更多相關Spring Security單點登入許可權內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45