日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

SpringBoot動態權限校驗:從零到一實現高效、優雅的解決方案

來源: 責編: 時間:2024-04-02 17:22:16 168觀看
導讀1、背景簡單先說一下需求吧,這樣也好讓看的人知道到底適不適合自己。實現自定義的登錄認證。登錄成功,生成token并將token 交由redis管理。登錄后對用戶訪問的接口進行接口級別權限認證。SpringSecurity提供的注解權限

1、背景

簡單先說一下需求吧,這樣也好讓看的人知道到底適不適合自己。
  • 實現自定義的登錄認證。
  • 登錄成功,生成token并將token 交由redis管理。
  • 登錄后對用戶訪問的接口進行接口級別權限認證。

SpringSecurity提供的注解權限校驗適合的場景是系統中僅有固定的幾個角色,且角色的憑證不可修改(如果修改需要改動代碼)。iQS28資訊網——每日最新資訊28at.com

@PreAuthorize("hasAuthority('ROLE_TELLER')") public Account post(Account account, double amount);

注:ROLE_TELLER是寫死的。iQS28資訊網——每日最新資訊28at.com

后端系統的訪問請求有以下幾種類型:iQS28資訊網——每日最新資訊28at.com

  • 登錄、登出(可自定義url)
  • 匿名用戶可訪問的接口(靜態資源,demo示例等)
  • 其他接口(在登錄的前提下,繼續判斷訪問者是否有權限訪問)

2、環境搭建

依賴引入,包括SpringSecurity、Redis、RedisSession需要的依賴:
<!--springSecurity安全框架--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-security</artifactId>    <version>2.3.4.RELEASE</version></dependency><!-- 默認通過SESSIONId改為通過請求頭與redis配合驗證session --><dependency>    <groupId>org.springframework.session</groupId>    <artifactId>spring-session-data-redis</artifactId>    <version>2.3.1.RELEASE</version></dependency><!--redis支持--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId>    <version>2.3.4.RELEASE</version></dependency>

注:springBoot版本也是2.3.4.RELEASE,如果有版本對應問題,自行解決。有用到swagger,為了便于測試。iQS28資訊網——每日最新資訊28at.com

新建springSecurity配置類

繼承自 WebSecurityConfigurerAdapter,過濾匿名用戶可訪問的接口。

WebSecurityConfig作為springSecurity的主配置文件。iQS28資訊網——每日最新資訊28at.com

@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {        /**     * Swagger等靜態資源不進行攔截     */    @Override    public void configure(WebSecurity web) {        web.ignoring().antMatchers(                "/*.html",                "/favicon.ico",                "/**/*.html",                "/**/*.css",                "/**/*.js",                "/error",                "/webjars/**",                "/resources/**",                "/swagger-ui.html",                "/swagger-resources/**",                "/v2/api-docs");    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                //配置一些不需要登錄就可以訪問的接口                .antMatchers("/demo/**", "/about/**").permitAll()                //任何尚未匹配的URL只需要用戶進行身份驗證                .anyRequest().authenticated()                .and()                .formLogin()//允許用戶進行基于表單的認證                .loginPage("/mylogin");    }}

注:證明可以訪問靜態資源不會被攔截iQS28資訊網——每日最新資訊28at.com

自定義登錄認證

springSecurity是基于過濾器進行安全認證的。

我們需要自定義:iQS28資訊網——每日最新資訊28at.com

  • 登錄過濾器:負責過濾登錄請求,再交由自定義的登錄認證管理器處理。
  • 登錄成功處理類:顧名思義,登錄成功后的一些處理(設置返回信息提示“登錄成功!”,返回數據類型為json)。
  • 登錄失敗處理類:類似登錄成功處理類。ps:登錄成功處理類和失敗處理類有默認的實現可以不自定義。但是建議自定義,因為返回的信息為英文,一般情況不符合要求。
  • 登錄認證管理器:根據過濾器傳過來的登錄參數,進行登錄認證,認證后授權。

新建登錄成功處理類

需要實現 AuthenticationSuccessHandleriQS28資訊網——每日最新資訊28at.com

@Componentpublic class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationSuccessHandler.class);    @Override    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {        response.setContentType(MediaType.APPLICATION_JSON_VALUE);        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());        //登錄成功返回的認證體,具體格式在后面的登錄認證管理器中        String responseJson = JackJsonUtil.object2String(ResponseFactory.success(authentication));        if (LOGGER.isDebugEnabled()) {            LOGGER.debug("登錄成功!");        }        response.getWriter().write(responseJson);    }}

新建登錄失敗處理類

實現 AuthenticationFailureHandleriQS28資訊網——每日最新資訊28at.com

@Componentpublic class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationFailureHandler.class);    @Override    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {        String errorMsg;        if (StringUtils.isNotBlank(e.getMessage())) {            errorMsg = e.getMessage();        } else {            errorMsg = CodeMsgEnum.LOG_IN_FAIL.getMsg();        }        response.setContentType(MediaType.APPLICATION_JSON_VALUE);        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());        String responseJson = JackJsonUtil.object2String(ResponseFactory.fail(CodeMsgEnum.LOG_IN_FAIL,errorMsg));        if (LOGGER.isDebugEnabled()) {            LOGGER.debug("認證失敗!");        }        response.getWriter().write(responseJson);    }}

新建登錄認證管理器

實現 AuthenticationProvider ,負責具體的身份認證(一般數據庫認證,在登錄過濾器過濾掉請求后傳入)iQS28資訊網——每日最新資訊28at.com

@Componentpublic class UserVerifyAuthenticationProvider implements AuthenticationProvider {    private PasswordEncoder passwordEncoder;    @Autowired    private UserService userService;    @Override    public Authentication authenticate(Authentication authentication) throws AuthenticationException {        String userName = (String) authentication.getPrincipal(); // Principal 主體,一般指用戶名        String passWord = (String) authentication.getCredentials(); //Credentials 網絡憑證,一般指密碼        //通過賬號去數據庫查詢用戶以及用戶擁有的角色信息        UserRoleVo userRoleVo = userService.findUserRoleByAccount(userName);        //數據庫密碼        String encodedPassword = userRoleVo.getPassWord();        //credentials憑證即為前端傳入密碼,因為前端一般用Base64加密過所以需要解密。        String credPassword = new String(Base64Utils.decodeFromString(passWord), StandardCharsets.UTF_8);        // 驗證密碼:前端明文,數據庫密文        passwordEncoder = new MD5Util();        if (!passwordEncoder.matches(credPassword, encodedPassword)) {            throw new AuthenticationServiceException("賬號或密碼錯誤!");        }        //ps:GrantedAuthority對認證主題的應用層面的授權,含當前用戶的權限信息,通常使用角色表示        List<GrantedAuthority> roles = new LinkedList<>();        List<Role> roleList = userRoleVo.getRoleList();        roleList.forEach(role -> {            SimpleGrantedAuthority roleId = new SimpleGrantedAuthority(role.getRoleId().toString());            roles.add(roleId);        });        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, passWord, roles);        token.setDetails(userRoleVo);//這里可以放用戶的詳細信息        return token;    }    @Override    public boolean supports(Class<?> authentication) {        return false;    }}

新建登錄過濾器

LoginFilter.java繼承UsernamePasswordAuthenticationFilter,負責過濾登錄請求并交由登錄認證管理器進行具體的認證。iQS28資訊網——每日最新資訊28at.com

public class LoginFilter extends UsernamePasswordAuthenticationFilter {    private UserVerifyAuthenticationProvider authenticationManager;    /**     * @param authenticationManager 認證管理器     * @param successHandler 認證成功處理類     * @param failureHandler 認證失敗處理類     */    public LoginFilter(UserVerifyAuthenticationProvider authenticationManager,                       CustomAuthenticationSuccessHandler successHandler,                       CustomAuthenticationFailureHandler failureHandler) {        //設置認證管理器(對登錄請求進行認證和授權)        this.authenticationManager = authenticationManager;        //設置認證成功后的處理類        this.setAuthenticationSuccessHandler(successHandler);        //設置認證失敗后的處理類        this.setAuthenticationFailureHandler(failureHandler);        //可以自定義登錄請求的url        super.setFilterProcessesUrl("/myLogin");    }    @Override    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {        try {            //轉換請求入參            UserDTO loginUser = new ObjectMapper().readValue(request.getInputStream(), UserDTO.class);            //入參傳入認證管理器進行認證            return authenticationManager.authenticate(                    new UsernamePasswordAuthenticationToken(loginUser.getUserName(), loginUser.getPassWord())            );        } catch (IOException e) {            e.printStackTrace();            return null;        }    }}

最后配置到WebSecurityConfig中:iQS28資訊網——每日最新資訊28at.com

@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private UserVerifyAuthenticationProvider authenticationManager;//認證用戶類    @Autowired    private CustomAuthenticationSuccessHandler successHandler;//登錄認證成功處理類    @Autowired    private CustomAuthenticationFailureHandler failureHandler;//登錄認證失敗處理類    /**     * Swagger等靜態資源不進行攔截     */    @Override    public void configure(WebSecurity web) {        web.ignoring().antMatchers(                "/*.html",                "/favicon.ico",                "/**/*.html",                "/**/*.css",                "/**/*.js",                "/error",                "/webjars/**",                "/resources/**",                "/swagger-ui.html",                "/swagger-resources/**",                "/v2/api-docs");    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                //配置一些不需要登錄就可以訪問的接口                .antMatchers("/demo/**", "/about/**").permitAll()                //任何尚未匹配的URL只需要用戶進行身份驗證                .anyRequest().authenticated()                .and()                //配置登錄過濾器                .addFilter(new LoginFilter(authenticationManager, successHandler, failureHandler))                .csrf().disable();    }}
驗證配置

訪問登錄請求:iQS28資訊網——每日最新資訊28at.com

圖片iQS28資訊網——每日最新資訊28at.com

成功進入LoginFilteriQS28資訊網——每日最新資訊28at.com

圖片圖片iQS28資訊網——每日最新資訊28at.com

安全頭和登錄返回token

依賴已經引入了,設置session由redis存儲,只需要如下圖配置:
session:    store-type: redis    redis:      namespace: spring:session:admin    # session 無操作失效時間 30 分鐘    timeout: 1800

設置token放入返回的header中需要在WebSecurityConfig中加入iQS28資訊網——每日最新資訊28at.com

/** * 配置 HttpSessionIdResolver Bean * 登錄之后將會在 Response Header x-auth-token 中 返回當前 sessionToken * 將token存儲在前端 每次調用的時候 Request Header x-auth-token 帶上 sessionToken */@Beanpublic HttpSessionIdResolver httpSessionIdResolver() {    return HeaderHttpSessionIdResolver.xAuthToken();}

關于安全頭信息可以參考:iQS28資訊網——每日最新資訊28at.com

  • https://docs.spring.io/spring-security/site/docs/5.2.1.BUILD-SNAPSHOT/reference/htmlsingle/#ns-headers

安全請求頭需要設置WebSecurityConfig中加入iQS28資訊網——每日最新資訊28at.com

protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                //配置一些不需要登錄就可以訪問的接口                .antMatchers("/demo/**", "/about/**").permitAll()                //任何尚未匹配的URL只需要用戶進行身份驗證                .anyRequest().authenticated()                .and()                //配置登錄過濾器                .addFilter(new LoginFilter(authenticationManager, successHandler, failureHandler))                .csrf().disable();        //配置頭部        http.headers()                .contentTypeOptions()                .and()                .xssProtection()                .and()                //禁用緩存                .cacheControl()                .and()                .httpStrictTransportSecurity()                .and()                //禁用頁面鑲嵌frame劫持安全協議  // 防止iframe 造成跨域                .frameOptions().disable();    }

進行登錄測試,驗證結果:iQS28資訊網——每日最新資訊28at.com

圖片圖片iQS28資訊網——每日最新資訊28at.com

注:響應中有tokeniQS28資訊網——每日最新資訊28at.com

查看redis。成功保存進了redisiQS28資訊網——每日最新資訊28at.com

圖片圖片iQS28資訊網——每日最新資訊28at.com

接口權限校驗

Spring Security使用FilterSecurityInterceptor過濾器來進行URL權限校驗,實際使用流程大致如下:iQS28資訊網——每日最新資訊28at.com

正常情況的接口權限判斷:iQS28資訊網——每日最新資訊28at.com

返回那些可以訪問當前url的角色

1、定義一個MyFilterInvocationSecurityMetadataSource實現FilterInvocationSecurityMetadataSource類,重寫getAttributes方法。iQS28資訊網——每日最新資訊28at.com

方法的作用是:返回哪些角色可以訪問當前url,這個肯定是從數據庫中獲取。要注意的是對于PathVariable傳參的url,數據庫中存的是這樣的:/getUserByName/{name}。但實際訪問的url中name是具體的值。類似的/user/getUserById 也可以匹配 /user/getUserById?1。iQS28資訊網——每日最新資訊28at.com

package com.aliyu.security.provider;import com.aliyu.service.role.RoleService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityConfig;import org.springframework.security.web.FilterInvocation;import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;import java.util.Collection;import java.util.List;import java.util.Map;/** *@create: *@description: 第一步:數據庫查詢所有權限出來: * 之所以要所有權限,因為數據庫url和實際請求url并不能直接匹配需要。比方:/user/getUserById 匹配 /user/getUserById?1 * 第二步:通過httpUrl匹配器找出允許訪問當前請求的角色列表(哪些角色可以訪問此請求) */@Componentpublic class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {    @Autowired    private RoleService roleService;    /**     * 返回當前URL允許訪問的角色列表     * @param object     * @return     * @throws IllegalArgumentException     */    @Override    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {        //入參轉為HttpServletRequest        FilterInvocation fi = (FilterInvocation) object;        HttpServletRequest request = fi.getRequest();        //從數據庫中查詢系統所有的權限,格式為<"權限url","能訪問url的逗號分隔的roleid">        List<Map<String, String>> allUrlRoleMap = roleService.getAllUrlRoleMap();        for (Map<String, String> urlRoleMap : allUrlRoleMap) {            String url = urlRoleMap.get("url");            String roles = urlRoleMap.get("roles");            //new AntPathRequestMatcher創建httpUrl匹配器:里面url匹配規則已經給我們弄好了,            // 能夠支持校驗PathVariable傳參的url(例如:/getUserByName/{name})            // 也能支持 /user/getUserById 匹配 /user/getUserById?1            AntPathRequestMatcher matcher = new AntPathRequestMatcher(url);            if (matcher.matches(request)){ //當前請求與httpUrl匹配器進行匹配                return SecurityConfig.createList(roles.split(","));            }        }        return null;    }    @Override    public Collection<ConfigAttribute> getAllConfigAttributes() {        return null;    }    @Override    public boolean supports(Class<?> clazz) {        return FilterInvocation.class.isAssignableFrom(clazz);    }}

注:iQS28資訊網——每日最新資訊28at.com

    1. 方案一是初始化的時候加載所有權限,一次就好了。iQS28資訊網——每日最新資訊28at.com

    2. 方案二每次請求都會去重新加載系統所有權限,好處就是不用擔心權限修改的問題。(本次實現方案)iQS28資訊網——每日最新資訊28at.com

    3. 方案三利用Redis緩存iQS28資訊網——每日最新資訊28at.com

判斷當前用戶是否擁有訪問當前url的角色

定義一個MyAccessDecisionManager:通過實現AccessDecisionManager接口自定義一個決策管理器,判斷是否有訪問權限。上一步MyFilterInvocationSecurityMetadataSource中返回的當前請求可以訪問角色列表會傳到這里的decide方法里面(如果沒有角色的話,不會進入decide方法。iQS28資訊網——每日最新資訊28at.com

正常情況你訪問的url必然和某個角色關聯,如果沒有關聯就不應該可以訪問)。decide方法傳了當前登錄用戶擁有的角色,通過判斷用戶擁有的角色中是否有一個角色和當前url可以訪問的角色匹配。如果匹配,權限校驗通過。iQS28資訊網——每日最新資訊28at.com

package com.aliyu.security.provider;import org.apache.commons.lang3.StringUtils;import org.springframework.security.access.AccessDecisionManager;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.authentication.AnonymousAuthenticationToken;import org.springframework.security.authentication.InsufficientAuthenticationException;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.web.FilterInvocation;import org.springframework.stereotype.Component;import java.util.Collection;import java.util.Iterator;/** *@create: *@description: 接口權限判斷(根據MyFilterInvocationSecurityMetadataSource獲取到的請求需要的角色 * 和當前登錄人的角色進行比較) */@Componentpublic class MyAccessDecisionManager implements AccessDecisionManager {    @Override    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {        //循環請求需要的角色,只要當前用戶擁有的角色中包含請求需要的角色中的一個,就算通過。        Iterator<ConfigAttribute> iterator = configAttributes.iterator();        while(iterator.hasNext()){            ConfigAttribute configAttribute = iterator.next();            String needCode = configAttribute.getAttribute();            //獲取到了登錄用戶的所有角色            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();            for (GrantedAuthority authority : authorities) {                if (StringUtils.equals(authority.getAuthority(), needCode)) {                    return;                }            }        }        throw new AccessDeniedException("當前訪問沒有權限");    }    @Override    public boolean supports(ConfigAttribute attribute) {        return false;    }    @Override    public boolean supports(Class<?> clazz) {        return FilterInvocation.class.isAssignableFrom(clazz);    }}
處理匿名用戶訪問無權限資源

1、定義一個CustomAuthenticationEntryPoint實現AuthenticationEntryPoint處理匿名用戶訪問無權限資源(可以理解為未登錄的用戶訪問,確實有些接口是可以不登錄也能訪問的,比較少,我們在WebSecurityConfig已經配置過了。如果多的話,需要另外考慮從數據庫中獲取,并且權限需要加一個標志它為匿名用戶可訪問)。iQS28資訊網——每日最新資訊28at.com

package com.aliyu.security.handler;import com.aliyu.common.util.JackJsonUtil;import com.aliyu.entity.common.vo.ResponseFactory;import com.aliyu.security.constant.MessageConstant;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.MediaType;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.nio.charset.StandardCharsets;import static com.aliyu.entity.common.exception.CodeMsgEnum.MOVED_PERMANENTLY;/** * 未登錄重定向處理器 * <p> * 未登錄狀態下訪問需要登錄的接口 * * @author */public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);    @Override    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {        response.setContentType(MediaType.APPLICATION_JSON_VALUE);        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());        //原來不需要登錄的接口,現在需要登錄了,所以叫永久移動        String message = JackJsonUtil.object2String(                ResponseFactory.fail(MOVED_PERMANENTLY, MessageConstant.NOT_LOGGED_IN)        );        if (LOGGER.isDebugEnabled()) {            LOGGER.debug("未登錄重定向!");        }        response.getWriter().write(message);    }}
處理登陸認證過的用戶訪問無權限資源

2、定義一個CustomAccessDeniedHandler 實現AccessDeniedHandler處理登陸認證過的用戶訪問無權限資源。iQS28資訊網——每日最新資訊28at.com

package com.aliyu.security.handler;import com.aliyu.common.util.JackJsonUtil;import com.aliyu.entity.common.exception.CodeMsgEnum;import com.aliyu.entity.common.vo.ResponseFactory;import com.aliyu.security.constant.MessageConstant;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.MediaType;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.nio.charset.StandardCharsets;/** * 拒絕訪問處理器(登錄狀態下,訪問沒有權限的方法時會進入此處理器) * * @author */public class CustomAccessDeniedHandler implements AccessDeniedHandler {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);    @Override    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException {        response.setContentType(MediaType.APPLICATION_JSON_VALUE);        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());        String message = JackJsonUtil.object2String(                ResponseFactory.fail(CodeMsgEnum.UNAUTHORIZED, MessageConstant.NO_ACCESS)        );        if(LOGGER.isDebugEnabled()){            LOGGER.debug("沒有權限訪問!");        }        response.getWriter().write(message);    }}

配置到WebSecurityConfigiQS28資訊網——每日最新資訊28at.com

package com.aliyu.security.config;import com.aliyu.filter.LoginFilter;import com.aliyu.security.handler.*;import com.aliyu.security.provider.MyAccessDecisionManager;import com.aliyu.security.provider.MyFilterInvocationSecurityMetadataSource;import com.aliyu.security.provider.UserVerifyAuthenticationProvider;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.ObjectPostProcessor;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.builders.WebSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;import org.springframework.session.web.http.HeaderHttpSessionIdResolver;import org.springframework.session.web.http.HttpSessionIdResolver;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private UserVerifyAuthenticationProvider authenticationManager;//認證用戶類    @Autowired    private CustomAuthenticationSuccessHandler successHandler;//登錄認證成功處理類    @Autowired    private CustomAuthenticationFailureHandler failureHandler;//登錄認證失敗處理類    @Autowired    private MyFilterInvocationSecurityMetadataSource securityMetadataSource;//返回當前URL允許訪問的角色列表    @Autowired    private MyAccessDecisionManager accessDecisionManager;//除登錄登出外所有接口的權限校驗    /**     * 密碼加密     * @return     */    @Bean    @ConditionalOnMissingBean(PasswordEncoder.class)    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }    /**     * 配置 HttpSessionIdResolver Bean     * 登錄之后將會在 Response Header x-auth-token 中 返回當前 sessionToken     * 將token存儲在前端 每次調用的時候 Request Header x-auth-token 帶上 sessionToken     */    @Bean    public HttpSessionIdResolver httpSessionIdResolver() {        return HeaderHttpSessionIdResolver.xAuthToken();    }    /**     * Swagger等靜態資源不進行攔截     */    @Override    public void configure(WebSecurity web) {        web.ignoring().antMatchers(                "/*.html",                "/favicon.ico",                "/**/*.html",                "/**/*.css",                "/**/*.js",                "/error",                "/webjars/**",                "/resources/**",                "/swagger-ui.html",                "/swagger-resources/**",                "/v2/api-docs");    }    @Override    protected void configure(HttpSecurity http) throws Exception {        http.authorizeRequests()                //配置一些不需要登錄就可以訪問的接口                .antMatchers("/demo/**", "/about/**").permitAll()                //任何尚未匹配的URL只需要用戶進行身份驗證                .anyRequest().authenticated()                //登錄后的接口權限校驗                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {                    @Override                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {                        object.setAccessDecisionManager(accessDecisionManager);                        object.setSecurityMetadataSource(securityMetadataSource);                        return object;                    }                })                .and()                //配置登出處理                .logout().logoutUrl("/logout")                .logoutSuccessHandler(new CustomLogoutSuccessHandler())                .clearAuthentication(true)                .and()                //用來解決匿名用戶訪問無權限資源時的異常                .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())                //用來解決登陸認證過的用戶訪問無權限資源時的異常                .accessDeniedHandler(new CustomAccessDeniedHandler())                .and()                //配置登錄過濾器                .addFilter(new LoginFilter(authenticationManager, successHandler, failureHandler))                .csrf().disable();        //配置頭部        http.headers()                .contentTypeOptions()                .and()                .xssProtection()                .and()                //禁用緩存                .cacheControl()                .and()                .httpStrictTransportSecurity()                .and()                //禁用頁面鑲嵌frame劫持安全協議  // 防止iframe 造成跨域                .frameOptions().disable();    }}

3、其他

特別的,我們認為如果一個接口屬于當前系統,那么它就應該有對應可以訪問的角色。這樣的接口才會被我們限制住。如果一個接口只是在當前系統定義了,而沒有指明它的角色,這樣的接口是不會被我們限制的。iQS28資訊網——每日最新資訊28at.com

注意點iQS28資訊網——每日最新資訊28at.com

下面的代碼,本意是想配置一些不需要登錄也可以訪問的接口。iQS28資訊網——每日最新資訊28at.com

圖片圖片iQS28資訊網——每日最新資訊28at.com

但是測試的時候發現,任何接口的調用都會進入這里MyFilterInvocationSecurityMetadataSource getAttriButes方法,包括我webSecurityConfig里配置的不需要登錄的url。結果就是不需要登錄的url和沒有配置角色的接口權限一樣待遇,要么都能訪問,要么都不能訪問!!!iQS28資訊網——每日最新資訊28at.com

圖片iQS28資訊網——每日最新資訊28at.com

所以如上圖,我在這里配置了不需要登錄的接口(因為不知道如何從webSercurityConfig中獲取,干脆就配置在這里了),去掉了webSercurityConfig中的相應配置。iQS28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-80872-0.htmlSpringBoot動態權限校驗:從零到一實現高效、優雅的解決方案

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 一篇解決單頁面應用首屏調優問題

下一篇: C++中時間相關函數用法詳解

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 沧源| 栖霞市| 鹿邑县| 宜春市| 叙永县| 车致| 芜湖市| 高安市| 贵德县| 仪陇县| 塔河县| 平陆县| 民勤县| 龙游县| 双流县| 讷河市| 湾仔区| 霍邱县| 沾益县| 涟水县| 荣成市| 区。| 余姚市| 盐边县| 沂源县| 巫溪县| 白玉县| 高台县| 宜宾市| 龙岩市| 嘉鱼县| 岗巴县| 绥棱县| 台湾省| 东海县| 堆龙德庆县| 临夏县| 铜鼓县| 浦北县| 青浦区| 兰考县|