日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring-security权限控制详解

發布時間:2024/4/15 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring-security权限控制详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在本例中,主要講解spring-boot與spring-security的集成,實現方式為:

  • 將用戶、權限、資源(url)采用數據庫存儲
  • 自定義過濾器,代替原有的 FilterSecurityInterceptor
  • 自定義實現 UserDetailsService、AccessDecisionManager和InvocationSecurityMetadataSourceService,并在配置文件進行相應的配置
    GitHub 地址:https://github.com/fp2952/spring-boot-security-demo

用戶角色表(基于RBAC權限控制)

  • 用戶表(base_user)
codetypelength
IDvarchar32
USER_NAMEvarchar50
USER_PASSWORDvarchar100
NIKE_NAMEvarchar50
STATUSint11
  • 用戶角色表(base_user_role)
codetypelength
IDvarchar32
USER_IDvarchar32
ROLE_IDvarchar32
  • 角色表(base_role)
codetypelength
IDvarchar32
ROLE_CODEvarchar32
ROLE_NAMEvarchar64
  • 角色菜單表(base_role_menu)
codetypelength
IDvarchar32
ROLE_IDvarchar32
MENU_IDvarchar32
  • 菜單表(base_menu)
codetypelength
IDvarchar32
MENU_URLvarchar120
MENU_SEQvarchar120
MENU_PARENT_IDvarchar32
MENU_NAMEvarchar50
MENU_ICONvarchar20
MENU_ORDERint11
IS_LEAFvarchar20

實現主要配置類

實現AbstractAuthenticationProcessingFilter

用于用戶表單驗證,內部調用了authenticationManager完成認證,根據認證結果執行successfulAuthentication或者unsuccessfulAuthentication,無論成功失敗,一般的實現都是轉發或者重定向等處理。

@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {if (postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//獲取表單中的用戶名和密碼String username = obtainUsername(request);String password = obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();//組裝成username+password形式的tokenUsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Allow subclasses to set the "details" propertysetDetails(request, authRequest);//交給內部的AuthenticationManager去認證,并返回認證信息return this.getAuthenticationManager().authenticate(authRequest);}

AuthenticationManager

AuthenticationManager是一個用來處理認證(Authentication)請求的接口。在其中只定義了一個方法authenticate(),該方法只接收一個代表認證請求的Authentication對象作為參數,如果認證成功,則會返回一個封裝了當前用戶權限等信息的Authentication對象進行返回。
Authentication authenticate(Authentication authentication) throws AuthenticationException;
在Spring Security中,AuthenticationManager的默認實現是ProviderManager,而且它不直接自己處理認證請求,而是委托給其所配置的AuthenticationProvider列表,然后會依次使用每一個AuthenticationProvider進行認證,如果有一個AuthenticationProvider認證后的結果不為null,則表示該AuthenticationProvider已經認證成功,之后的AuthenticationProvider將不再繼續認證。然后直接以該AuthenticationProvider的認證結果作為ProviderManager的認證結果。如果所有的AuthenticationProvider的認證結果都為null,則表示認證失敗,將拋出一個ProviderNotFoundException。
校驗認證請求最常用的方法是根據請求的用戶名加載對應的UserDetails,然后比對UserDetails的密碼與認證請求的密碼是否一致,一致則表示認證通過。
Spring Security內部的DaoAuthenticationProvider就是使用的這種方式。其內部使用UserDetailsService來負責加載UserDetails。在認證成功以后會使用加載的UserDetails來封裝要返回的Authentication對象,加載的UserDetails對象是包含用戶權限等信息的。認證成功返回的Authentication對象將會保存在當前的SecurityContext中。

實現UserDetailsService

UserDetailsService只定義了一個方法 loadUserByUsername,根據用戶名可以查到用戶并返回的方法。

@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {logger.debug("權限框架-加載用戶");List<GrantedAuthority> auths = new ArrayList<>();BaseUser baseUser = new BaseUser();baseUser.setUserName(username);baseUser = baseUserService.selectOne(baseUser);if (baseUser == null) {logger.debug("找不到該用戶 用戶名:{}", username);throw new UsernameNotFoundException("找不到該用戶!");}if(baseUser.getStatus()==2){logger.debug("用戶被禁用,無法登陸 用戶名:{}", username);throw new UsernameNotFoundException("用戶被禁用!");}List<BaseRole> roles = baseRoleService.selectRolesByUserId(baseUser.getId());if (roles != null) {//設置角色名稱for (BaseRole role : roles) {SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleCode());auths.add(authority);}}return new org.springframework.security.core.userdetails.User(baseUser.getUserName(), baseUser.getUserPassword(), true, true, true, true, auths);}

實現AbstractSecurityInterceptor

訪問url時,會被AbstractSecurityInterceptor攔截器攔截,然后調用FilterInvocationSecurityMetadataSource的方法來獲取被攔截url所需的全部權限,再調用授權管理器AccessDecisionManager鑒權。

public class CustomSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {private FilterInvocationSecurityMetadataSource securityMetadataSource;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}@Overridepublic void destroy() {}@Overridepublic Class<?> getSecureObjectClass() {return FilterInvocation.class;}@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}public void invoke(FilterInvocation fi) throws IOException {InterceptorStatusToken token = super.beforeInvocation(fi);try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());} catch (ServletException e) {super.afterInvocation(token, null);}}public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {return securityMetadataSource;}public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {this.securityMetadataSource = securityMetadataSource;} }

FilterInvocationSecurityMetadataSource 獲取所需權限

@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {//獲取當前訪問urlString url = ((FilterInvocation) object).getRequestUrl();int firstQuestionMarkIndex = url.indexOf("?");if (firstQuestionMarkIndex != -1) {url = url.substring(0, firstQuestionMarkIndex);}List<ConfigAttribute> result = new ArrayList<>();try {//設置不攔截if (propertySourceBean.getProperty("security.ignoring") != null) {String[] paths = propertySourceBean.getProperty("security.ignoring").toString().split(",");//判斷是否符合規則for (String path: paths) {String temp = StringUtil.clearSpace(path);if (matcher.match(temp, url)) {return SecurityConfig.createList("ROLE_ANONYMOUS");}}}//如果不是攔截列表里的, 默認需要ROLE_ANONYMOUS權限if (!isIntercept(url)) {return SecurityConfig.createList("ROLE_ANONYMOUS");}//查詢數據庫url匹配的菜單List<BaseMenu> menuList = baseMenuService.selectMenusByUrl(url);if (menuList != null && menuList.size() > 0) {for (BaseMenu menu : menuList) {//查詢擁有該菜單權限的角色列表List<BaseRole> roles = baseRoleService.selectRolesByMenuId(menu.getId());if (roles != null && roles.size() > 0) {for (BaseRole role : roles) {ConfigAttribute conf = new SecurityConfig(role.getRoleCode());result.add(conf);}}}}} catch (Exception e) {e.printStackTrace();}return result;}/*** 判斷是否需要過濾* @param url* @return*/public boolean isIntercept(String url) {String[] filterPaths = propertySourceBean.getProperty("security.intercept").toString().split(",");for (String filter: filterPaths) {if (matcher.match(StringUtil.clearSpace(filter), url) & !matcher.match(indexUrl, url)) {return true;}}return false;}

AccessDecisionManager 鑒權

@Overridepublic void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {if (collection == null) {return;}for (ConfigAttribute configAttribute : collection) {String needRole = configAttribute.getAttribute();for (GrantedAuthority ga : authentication.getAuthorities()) {if (needRole.trim().equals(ga.getAuthority().trim()) || needRole.trim().equals("ROLE_ANONYMOUS")) {return;}}}throw new AccessDeniedException("無權限!");}

配置 WebSecurityConfigurerAdapter

/*** spring-security配置*/ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate PropertySource propertySourceBean;@Overrideprotected void configure(HttpSecurity http) throws Exception {logger.debug("權限框架配置");String[] paths = null;//設置不攔截if (propertySourceBean.getProperty("security.ignoring") != null) {paths = propertySourceBean.getProperty("security.ignoring").toString().split(",");paths = StringUtil.clearSpace(paths);}//設置過濾器http // 根據配置文件放行無需驗證的url.authorizeRequests().antMatchers(paths).permitAll().and().httpBasic()// 配置驗證異常處理.authenticationEntryPoint(getCustomLoginAuthEntryPoint())// 配置登陸過濾器.and().addFilterAt(getCustomLoginFilter(), UsernamePasswordAuthenticationFilter.class)// 配置 AbstractSecurityInterceptor.addFilterAt(getCustomSecurityInterceptor(), FilterSecurityInterceptor.class)// 登出成功處理.logout().logoutSuccessHandler(getCustomLogoutSuccessHandler())// 關閉csrf.and().csrf().disable()// 其他所有請求都需要驗證.authorizeRequests().anyRequest().authenticated()// 配置登陸url, 登陸頁面并無需驗證.and().formLogin().loginProcessingUrl("/login").loginPage("/login.ftl").permitAll()// 登出.and().logout().logoutUrl("/logout").permitAll();logger.debug("配置忽略驗證url");}@Autowired@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(getDaoAuthenticationProvider());}/*** spring security 配置* @return*/@Beanpublic CustomLoginAuthEntryPoint getCustomLoginAuthEntryPoint() {return new CustomLoginAuthEntryPoint();}/*** 用戶驗證* @return*/@Beanpublic DaoAuthenticationProvider getDaoAuthenticationProvider() {DaoAuthenticationProvider provider = new DaoAuthenticationProvider();provider.setUserDetailsService(userDetailsService);provider.setHideUserNotFoundExceptions(false);provider.setPasswordEncoder(new BCryptPasswordEncoder());return provider;}/*** 登陸* @return*/@Beanpublic CustomLoginFilter getCustomLoginFilter() {CustomLoginFilter filter = new CustomLoginFilter();try {filter.setAuthenticationManager(this.authenticationManagerBean());} catch (Exception e) {e.printStackTrace();}filter.setAuthenticationSuccessHandler(getCustomLoginAuthSuccessHandler());filter.setAuthenticationFailureHandler(new CustomLoginAuthFailureHandler());return filter;}@Beanpublic CustomLoginAuthSuccessHandler getCustomLoginAuthSuccessHandler() {CustomLoginAuthSuccessHandler handler = new CustomLoginAuthSuccessHandler();if (propertySourceBean.getProperty("security.successUrl")!=null){handler.setAuthSuccessUrl(propertySourceBean.getProperty("security.successUrl").toString());}return handler;}/*** 登出* @return*/@Beanpublic CustomLogoutSuccessHandler getCustomLogoutSuccessHandler() {CustomLogoutSuccessHandler handler = new CustomLogoutSuccessHandler();if (propertySourceBean.getProperty("security.logoutSuccessUrl")!=null){handler.setLoginUrl(propertySourceBean.getProperty("security.logoutSuccessUrl").toString());}return handler;}/*** 過濾器* @return*/@Beanpublic CustomSecurityInterceptor getCustomSecurityInterceptor() {CustomSecurityInterceptor interceptor = new CustomSecurityInterceptor();interceptor.setAccessDecisionManager(new CustomAccessDecisionManager());interceptor.setSecurityMetadataSource(getCustomMetadataSourceService());try {interceptor.setAuthenticationManager(this.authenticationManagerBean());} catch (Exception e) {e.printStackTrace();}return interceptor;}@Beanpublic CustomMetadataSourceService getCustomMetadataSourceService() {CustomMetadataSourceService sourceService = new CustomMetadataSourceService();if (propertySourceBean.getProperty("security.successUrl")!=null){sourceService.setIndexUrl(propertySourceBean.getProperty("security.successUrl").toString());}return sourceService;} }

轉載于:https://www.cnblogs.com/fp2952/p/8933107.html

總結

以上是生活随笔為你收集整理的spring-security权限控制详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。