精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發 前端
LoginFilter.java繼承UsernamePasswordAuthenticationFilter,負責過濾登錄請求并交由登錄認證管理器進行具體的認證。

1、背景

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

SpringSecurity提供的注解權限校驗適合的場景是系統中僅有固定的幾個角色,且角色的憑證不可修改(如果修改需要改動代碼)。

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

注:ROLE_TELLER是寫死的。

后端系統的訪問請求有以下幾種類型:

  • 登錄、登出(可自定義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,為了便于測試。

新建springSecurity配置類

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

WebSecurityConfig作為springSecurity的主配置文件。

@Configuration
@EnableWebSecurity
public 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");
    }

}

注:證明可以訪問靜態資源不會被攔截

自定義登錄認證

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

我們需要自定義:

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

新建登錄成功處理類

需要實現 AuthenticationSuccessHandler

@Component
public 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);
    }
}

新建登錄失敗處理類

實現 AuthenticationFailureHandler

@Component
public 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 ,負責具體的身份認證(一般數據庫認證,在登錄過濾器過濾掉請求后傳入)

@Component
public 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,負責過濾登錄請求并交由登錄認證管理器進行具體的認證。

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中:

@Configuration
@EnableWebSecurity
public 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();
    }

}
驗證配置

訪問登錄請求:

圖片

成功進入LoginFilter

圖片圖片

安全頭和登錄返回token

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

設置token放入返回的header中需要在WebSecurityConfig中加入

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

關于安全頭信息可以參考:

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

安全請求頭需要設置WebSecurityConfig中加入

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();
    }

進行登錄測試,驗證結果:

圖片圖片

注:響應中有token

查看redis。成功保存進了redis

圖片圖片

接口權限校驗

Spring Security使用FilterSecurityInterceptor過濾器來進行URL權限校驗,實際使用流程大致如下:

正常情況的接口權限判斷:

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

1、定義一個MyFilterInvocationSecurityMetadataSource實現FilterInvocationSecurityMetadataSource類,重寫getAttributes方法。

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

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匹配器找出允許訪問當前請求的角色列表(哪些角色可以訪問此請求)
 */
@Component
public 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);
    }
}

注:

    1. 方案一是初始化的時候加載所有權限,一次就好了。

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

    3. 方案三利用Redis緩存

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

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

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

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獲取到的請求需要的角色
 * 和當前登錄人的角色進行比較)
 */
@Component
public 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已經配置過了。如果多的話,需要另外考慮從數據庫中獲取,并且權限需要加一個標志它為匿名用戶可訪問)。

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處理登陸認證過的用戶訪問無權限資源。

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);
    }


}

配置到WebSecurityConfig

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
@EnableWebSecurity
public 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、其他

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

注意點

下面的代碼,本意是想配置一些不需要登錄也可以訪問的接口。

圖片圖片

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

圖片

所以如上圖,我在這里配置了不需要登錄的接口(因為不知道如何從webSercurityConfig中獲取,干脆就配置在這里了),去掉了webSercurityConfig中的相應配置。

責任編輯:武曉燕 來源: 一安未來
相關推薦

2018-12-03 12:07:54

南京新動態解決方案

2025-11-18 07:52:13

2011-09-14 10:56:52

服務器虛擬化數據中心

2009-03-12 09:57:24

APC英飛VMware

2017-05-10 14:41:41

存儲

2014-06-09 17:01:06

智能云監控華為

2022-05-03 10:43:43

SpringJava

2009-12-16 13:39:27

Ruby元編程

2022-06-16 10:38:24

URL權限源代碼

2009-04-20 13:47:40

思杰Vmwareesx

2011-06-27 20:48:38

打印機解決方案

2013-01-07 11:05:29

華為解決方案寬帶接入

2021-07-06 13:32:34

零信任網絡安全網絡攻擊

2021-11-10 10:03:18

SpringBootJava代碼

2025-11-07 08:05:18

2024-01-26 08:49:47

ChatGPT搜索方式

2010-04-28 11:48:13

Oracle MySQ

2016-02-29 15:09:54

戴爾云計算

2021-01-12 10:43:22

數字化轉型IT云原生

2025-08-28 01:00:00

點贊
收藏

51CTO技術棧公眾號

欧美丝袜丝交足nylons图片| 91在线观看污| 欧美成人中文字幕| 污污免费在线观看| 成人爱爱网址| 亚洲欧美日韩在线播放| 精品国产一区二区三区免费| 欧美一区免费看| 最新精品国产| 日韩精品www| 人人干人人干人人| 精精国产xxxx视频在线中文版 | 国产一级片中文字幕| sqte在线播放| 国产欧美日韩激情| 国产精品二区三区| 一区二区的视频| 亚洲一区国产一区| 久久91亚洲人成电影网站| 亚洲av综合一区二区| 亚洲综合网狠久久| 欧美日精品一区视频| 国产中文字幕视频在线观看| 国内外激情在线| 欧美极品少妇xxxxⅹ高跟鞋 | 日本午夜精品理论片a级appf发布| 99久久99久久精品国产| 天天久久夜夜| 亚洲成av人片在线观看香蕉| 日韩欧美理论片| 精品三区视频| 日韩欧美一区视频| 日本www在线视频| av免费看在线| 亚洲欧洲日韩av| 亚洲高清不卡一区| 水莓100在线视频| 成人一区二区三区| 91网在线免费观看| 国产精品欧美综合亚洲| 男男视频亚洲欧美| 日本亚洲精品在线观看| 日韩欧美亚洲一区二区三区| 国产精品s色| 色青青草原桃花久久综合| 欧美多人猛交狂配| 亚洲免费毛片| 亚洲色图50p| 日本高清www| 伊人成综合网yiren22| 亚洲精品国产拍免费91在线| 人妻av一区二区| 国产日韩三级| 亚洲成色www8888| 国产人成视频在线观看| 国内精品免费| 亚洲精品视频在线播放| 国产精品1000部啪视频| 亚洲国产欧美日韩在线观看第一区 | 久久这里只有精品视频首页| 国产黄色录像片| 91国语精品自产拍| 欧美国产日产韩国视频| 国产午夜激情视频| 亚洲欧美日韩精品一区二区 | 久久99国产精品久久99大师| 精品国产乱码久久久久久夜甘婷婷| 苍井空张开腿实干12次| 国产精品自在线拍| 亚洲视频欧洲视频| 少妇视频一区二区| 欧美区一区二| 久久全国免费视频| 日韩免费av网站| 美女脱光内衣内裤视频久久影院| 91免费看国产| 欧美一级特黄aaaaaa大片在线观看| 成人午夜免费电影| 欧美在线播放一区二区| 国产写真视频在线观看| 香港成人在线视频| 亚欧在线免费观看| 日本免费精品| 日韩精品在线观看一区二区| 欧美xxxx精品| 一区久久精品| 国产精品久久综合av爱欲tv| www.日韩高清| 91蝌蚪porny九色| 亚洲欧美日韩精品综合在线观看| 亚洲电影视频在线| 日本久久一区二区| 色哟哟在线观看视频| 亚洲精品亚洲人成在线| xvideos成人免费中文版| 久久亚洲成人av| xx欧美撒尿嘘撒尿xx| 四虎在线免费看| 国产精品久久久久久久午夜片| 精品一区二区三区毛片| 亚洲永久av| 欧美一区二区国产| 自拍偷拍中文字幕| 欧美一区二区三区久久精品| 国产a∨精品一区二区三区不卡| 国产乱码精品一区二区| 91美女片黄在线观看91美女| 日本a级片在线观看| 欧美电影免费看| 精品久久人人做人人爽| 美女网站视频色| 亚洲视频二区| 国产乱人伦精品一区二区| 久操免费在线| 在线免费观看视频一区| 亚洲av成人精品一区二区三区| 91麻豆国产自产在线观看亚洲| 欧美一区深夜视频| 亚洲乱码国产乱码精品精软件| 中文字幕欧美日本乱码一线二线| 成熟了的熟妇毛茸茸| 日韩高清在线观看一区二区| 中文字幕精品www乱入免费视频| 欧美三日本三级少妇99| 丰满岳乱妇一区二区三区| 一区高清视频| jizz亚洲女人高潮大叫| 亚洲欧美国产一区二区三区| 日本少妇性高潮| 国产精品123区| 午夜啪啪免费视频| 欧美日韩国产网站| 亚洲最新av在线| 在线观看 亚洲| 久久久久亚洲蜜桃| 男人天堂999| 欧美色资源站| 97色在线观看| 人妻精品一区二区三区| 亚洲福利视频三区| 日本国产在线视频| 亚洲国产高清一区二区三区| 成人综合av网| 成人免费网站观看| 日韩av中文字幕在线| 日本三级理论片| 成人激情免费电影网址| 国产精品久久久久7777| 99ri日韩精品视频| 欧美激情一区二区三区成人| 亚洲欧美另类日韩| 午夜视频在线观看一区二区三区| 亚洲一区二区三区黄色| 99综合在线| 久久影院理伦片| 欧美成人精品三级网站| 中文字幕精品www乱入免费视频| 一区二区乱子伦在线播放| 中文字幕国产一区| 国产欧美精品一二三| 韩国一区二区三区在线观看| 国产伦精品一区二区三毛| 一本大道色婷婷在线| 亚洲一区二区久久| 一级黄色大片免费| 亚洲视频一二三区| www.美色吧.com| 欧美一级专区| 椎名由奈jux491在线播放| 欧美久久亚洲| 欧美在线激情网| a天堂中文在线| 日韩手机在线导航| 91精品国产乱码在线观看| 国产日韩影视精品| 色婷婷综合在线观看| 最新日韩在线| 天堂va久久久噜噜噜久久va| 国产精品亚洲欧美一级在线 | 粉嫩av性色av蜜臀av网站| 国产二区国产一区在线观看| av动漫在线看| 97久久夜色精品国产| 国产精品青青草| 日韩视频网站在线观看| 欧美另类在线播放| 欧美3p视频在线观看| 这里只有精品视频在线观看| 国产精品xxxx喷水欧美| 国产精品白丝在线| 欧美在线一级片| 久久精品国产99国产| 久在线观看视频| 国产精品久久久久久久| 久久99精品久久久久久秒播放器| 久久69成人| 8x海外华人永久免费日韩内陆视频| 色的视频在线免费看| 亚洲国产另类 国产精品国产免费| 国产裸体美女永久免费无遮挡| 亚洲一区二区三区视频在线播放| jizz中文字幕| av在线播放成人| 北条麻妃亚洲一区| 人人精品人人爱| 国产黄色一级网站| 午夜精品999| 亚洲在线观看一区| 香蕉国产成人午夜av影院| 亚洲在线视频观看| 成人精品国产亚洲| 欧美在线欧美在线| 91资源在线观看| 欧美xxxx综合视频| 欧美13一16娇小xxxx| 国产丝袜一区二区三区免费视频| 成人av手机在线| 欧美精品18+| 中国女人真人一级毛片| 欧美性jizz18性欧美| 中文字幕第28页| 亚洲精品伦理在线| 国产男女猛烈无遮挡在线喷水| 久久精品一区二区三区不卡| 熟妇人妻久久中文字幕| 国产99一区视频免费| 午夜视频在线观| 久久99久久99精品免视看婷婷| 国产极品美女高潮无套久久久| 亚洲国产免费看| 无码 制服 丝袜 国产 另类| 亚洲欧洲美洲一区二区三区| 亚洲一区在线直播| 天天揉久久久久亚洲精品| 亚洲一区二区不卡视频| 日本精品三区| 亚洲精品高清视频| 成人黄色av| 亚洲.欧美.日本.国产综合在线| 国产精品自拍区| 欧美一级二级三级| 少妇精品久久久| 日韩在线导航| 国产精品传媒精东影业在线| 一区二区精品在线| 亚洲电影影音先锋| 蜜臀在线免费观看| 欧美在线国产| 中国丰满熟妇xxxx性| 亚洲精品免费观看| av黄色在线网站| 天堂蜜桃91精品| 日本人视频jizz页码69| 激情小说亚洲一区| 免费黄色av网址| av一区二区久久| 女人又爽又黄免费女仆| 国产精品女同一区二区三区| 日韩在线视频免费看| 亚洲免费观看高清完整版在线| 欧美日韩国产精品一区二区三区| 夜夜嗨av一区二区三区| 五月婷婷中文字幕| 在线精品视频免费观看| 91国内精品视频| 精品久久国产老人久久综合| 午夜影院免费视频| 在线播放精品一区二区三区| 九义人在线观看完整免费版电视剧| 欧美伦理91i| 中文字幕影音在线| 91九色视频导航| 国产精品极品在线观看| 日韩av高清| 午夜久久福利| 日韩精品一区二区三区久久| 久久精品国产第一区二区三区| 三级黄色片免费看| 久久综合色8888| av黄色免费在线观看| 亚洲成人一区在线| 久久精品99北条麻妃| 欧美一级高清大全免费观看| 日本a一级在线免费播放| 日韩中文字幕亚洲| 天堂√8在线中文| 91九色在线视频| 真实原创一区二区影院| 国产成人三级视频| 男女精品网站| 美女流白浆视频| 日本一区二区视频在线| 免费人成视频在线| 欧美天堂亚洲电影院在线播放| 亚洲国产日韩在线观看| 国产一区二区三区高清在线观看| 羞羞污视频在线观看| 国产第一区电影| 国产精品白浆| 欧美日韩视频免费在线观看| 国产美女诱惑一区二区| 日本黄色三级网站| 国产欧美一区二区三区在线老狼| 久久亚洲精品大全| 69久久99精品久久久久婷婷| 精品乱码一区二区三四区视频| 欧美成人激情在线| 外国电影一区二区| 久久国产精品久久| 极品中文字幕一区| 992kp免费看片| 欧美韩国一区二区| 国产乱国产乱老熟| 精品国产一区二区精华 | 性欧美激情精品| 成人在线免费av| 青娱乐国产91| 亚洲一区国产| av鲁丝一区鲁丝二区鲁丝三区| 亚洲男女毛片无遮挡| 福利在线一区二区三区| 国产美女视频一区二区三区| 久久精品一本久久99精品| 综合日韩av| 九九九九精品九九九九| 国产精品v日韩精品v欧美精品网站| 国产精品自拍视频在线| 国产欧美日韩另类一区| 国产成人无码一区二区在线播放| 欧美精品一区男女天堂| 国模私拍视频在线播放| 91成人在线看| 欧美二区视频| 杨幂一区二区国产精品| 自拍偷拍亚洲激情| 国产农村老头老太视频| 精品国产拍在线观看| 欧美成a人片免费观看久久五月天| 日韩av电影免费在线| 日韩av电影天堂| 人成免费在线视频| 欧美日韩性生活| 久操视频在线免费播放| 91在线观看免费高清完整版在线观看| 99精品视频在线观看免费播放| 在线看的黄色网址| 国产精品久久久久影视| 国产精品久久久久久久成人午夜| 色偷偷噜噜噜亚洲男人| 亚洲国产综合在线观看| 欧美少妇在线观看| 国产99久久久久| 好看的av在线| 亚洲午夜激情免费视频| 成人四虎影院| 国产欧美自拍视频| 国产suv精品一区二区883| 97人人澡人人爽人人模亚洲 | 国产永久免费高清在线观看| 国产精品91视频| 亚洲精品国产成人影院| 91福利视频免费观看| 亚洲成人午夜影院| 你懂得网站在线| 国产日韩欧美日韩大片| 欧美不卡一区| 亚洲欧美日本一区| 欧日韩精品视频| www免费在线观看| 国产伦精品一区二区三区免费视频 | 91在线观看地址| 波多野结衣视频在线看| 久久精品国产电影| 国产精品白浆| 日日干夜夜操s8| 亚洲午夜久久久久久久久电影院| 午夜性色福利视频| 成人福利在线视频| 亚洲日本黄色| 青青青视频在线免费观看| 日韩片之四级片| 韩国主播福利视频一区二区三区| 亚洲精品一区二区三区蜜桃久 | 亚洲无人区码一码二码三码的含义| 欧美日韩久久一区| 白浆视频在线观看| 亚洲精品一区二区三区樱花| 国产99久久久国产精品潘金 | 日韩欧美视频一区二区| 国产毛片精品一区| 黄色片视频免费| 欧美成人激情视频免费观看| 少妇精品久久久一区二区| 欧美一级片在线免费观看| 日本道色综合久久| 欧美韩日亚洲| 一区二区三区免费看| av在线不卡网| www.中文字幕| 国产精品久久久久久久一区探花 |