欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot 訪問安全之認(rèn)證和鑒權(quán)詳解

 更新時間:2021年11月09日 14:17:39   作者:布道  
這篇文章主要介紹了Spring Boot 訪問安全之認(rèn)證和鑒權(quán),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

在web應(yīng)用中有大量場景需要對用戶進(jìn)行安全校,一般人的做法就是硬編碼的方式直接埋到到業(yè)務(wù)代碼中,但可曾想過這樣做法會導(dǎo)致代碼不夠簡潔(大量重復(fù)代碼)、有個性化時難維護(hù)(每個業(yè)務(wù)邏輯訪問控制策略都不相同甚至差異很大)、容易發(fā)生安全泄露(有些業(yè)務(wù)可能不需要當(dāng)前登錄信息,但被訪問的數(shù)據(jù)可能是敏感數(shù)據(jù)由于遺忘而沒有受到保護(hù))。

為了更安全、更方便的進(jìn)行訪問安全控制,我們可以想到的就是使用springmvc的攔截器(HandlerInterceptor),但其實更推薦使用更為成熟的spring security來完成認(rèn)證和鑒權(quán)。

攔截器

攔截器HandlerInterceptor確實可以幫我們完成登錄攔截、或是權(quán)限校驗、或是防重復(fù)提交等需求。其實基于它也可以實現(xiàn)基于url或方法級的安全控制。

如果你對spring mvc的請求處理流程相對的了解,它的原理容易理解。

public interface HandlerInterceptor {
	/**
	 * Intercept the execution of a handler. Called after HandlerMapping determined
	 * an appropriate handler object, but before HandlerAdapter invokes the handler.
	 * 
	 * 在業(yè)務(wù)處理器處理請求之前被調(diào)用。預(yù)處理,可以進(jìn)行編碼、安全控制、權(quán)限校驗等處理
	 * 
	 * handler:controller內(nèi)的方法,可以通過HandlerMethod method= ((HandlerMethod)handler);獲取到@RequestMapping
	 */	
	boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
 
	/**
	 * Intercept the execution of a handler. Called after HandlerAdapter actually
	 * invoked the handler, but before the DispatcherServlet renders the view.
	 * 
	 * 在業(yè)務(wù)處理器處理請求執(zhí)行完成后,生成視圖之前執(zhí)行。后處理(調(diào)用了Service并返回ModelAndView,但未進(jìn)行頁面渲染),有機會修改ModelAndView
	 */	
	void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;
 
	/**
	 * Callback after completion of request processing, that is, after rendering
	 * the view. Will be called on any outcome of handler execution, thus allows
	 * for proper resource cleanup.
	 * 
	 * 在DispatcherServlet完全處理完請求后被調(diào)用,可用于清理資源等。返回處理(已經(jīng)渲染了頁面)
	 * 
	 */	
	void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
//你可以基于有些url進(jìn)行攔截
@Configuration
public class UserSecurityInterceptor extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        String[] securityUrls = new String[]{"/**"};
        String[] excludeUrls = new String[]{"/**/esb/**", "/**/dictionary/**"};
        registry.addInterceptor(userLoginInterceptor()).excludePathPatterns(excludeUrls).addPathPatterns(securityUrls);
        super.addInterceptors(registry);
    }
 
    /** fixed: url中包含// 報錯
     * org.springframework.security.web.firewall.RequestRejectedException: The request was rejected because the URL was not normalized.
     * @return
     */
    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        DefaultHttpFirewall firewall = new DefaultHttpFirewall();
        firewall.setAllowUrlEncodedSlash(true);
        return firewall;
    }
 
    @Bean
    public AuthInterceptor userLoginInterceptor() {
        return new AuthInterceptor();
    }
 
    public class AuthInterceptor implements HandlerInterceptor {
        public Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
        @Autowired
        private ApplicationContext applicationContext; 
        public AuthInterceptor() {
        }
 
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            LoginUserInfo user = null;
            try {
                user = (LoginUserInfo) SSOUserUtils.getCurrentLoginUser();
            } catch (Exception e) {
                logger.error("從SSO登錄信息中獲取用戶信息失敗! 詳細(xì)錯誤信息:%s", e);
                throw new ServletException("從SSO登錄信息中獲取用戶信息失敗!", e);
            }
 
            String[] profiles = applicationContext.getEnvironment().getActiveProfiles();
            if (!Arrays.isNullOrEmpty(profiles)) {
                if ("dev".equals(profiles[0])) {
                    return true;
                }
            }
            if (user == null || UserUtils.ANONYMOUS_ROLE_ID.equals(user.getRoleId())) {
                throw new ServletException("獲取登錄用戶信息失??!");
            }
            return true;
        }
 
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 
        }
 
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 
        }
    }
}

認(rèn)證

確認(rèn)一個訪問請求發(fā)起的時候背后的用戶是誰,他的用戶信息是怎樣的。在spring security 里面認(rèn)證支持很多種方式,最簡單的就是用戶名密碼,還有LDAP、OpenID、CAS等等。

而在我們的系統(tǒng)里面,用戶信息需要通過kxtx-sso模塊進(jìn)行獲取。通過sso認(rèn)證比較簡單,就是要確認(rèn)用戶是否通過會員系統(tǒng)登錄,并把登錄信息包裝成授權(quán)對象放到SecurityContext中,通過一個filter來完成:

@Data
@EqualsAndHashCode(callSuper = false)
public class SsoAuthentication extends AbstractAuthenticationToken {
    private static final long serialVersionUID = -1799455508626725119L; 
    private LoginUserInfo user; 
    public SsoAuthentication(LoginUserInfo user) {
        super(null);
        this.user = user;
    }
 
    @Override
    public Object getCredentials() {
        return "kxsso";
    }
 
    @Override
    public Object getPrincipal() {
        return user;
    }
 
    @Override
    public String getName() {
        return user.getName();
    }
}
public class SsoAuthenticationProcessingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        LoginUserInfo user = (LoginUserInfo) SSOUserUtils.getCurrentLoginUser();
        SsoAuthentication auth = new SsoAuthentication(user );
        SecurityContextHolder.getContext().setAuthentication(auth);
        filterChain.doFilter(request, response);
    }
}
@Component
public class SsoAuthenticationProvider implements AuthenticationProvider {
 
    @Value("${env}")
    String env;
 
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        LoginUserInfo loginUserInfo = (LoginUserInfo) authentication.getPrincipal();
        /*
         * DEV環(huán)境允許匿名用戶訪問,方便調(diào)試,其他環(huán)境必須登錄。
         */
        if (!UserUtils.ANONYMOUS_ROLE_ID.equals(loginUserInfo.getRoleId()) || "dev".equals(env)) {
            authentication.setAuthenticated(true);
        } else {
            throw new BadCredentialsException("請登錄");
        }
        return authentication;
    }
 
    @Override
    public boolean supports(Class<?> authentication) {
        return SsoAuthentication.class.equals(authentication);
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
    protected void configure(HttpSecurity http) throws Exception {
        // 關(guān)閉session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and();
 
        // 允許訪問所有URL,通過方法保護(hù)的形式來限制訪問。
        http.authorizeRequests().anyRequest().permitAll();
 
        // 注冊sso filter
        http.addFilterBefore(ssoAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
    }
 
    @Bean
    SsoAuthenticationProcessingFilter ssoAuthenticationProcessingFilter() {
        return new SsoAuthenticationProcessingFilter();
    }
}

鑒權(quán)

控制一個功能是否能被當(dāng)前用戶訪問,對不符合要求的用戶予以拒絕。spring security 主要有兩種控制點:

  • 基于請求路徑的:控制某一URL模式必須符合某種要求;
  • 基于方法的:控制某一方法必須符合某種要求;

而控制形式就比較多樣化了:

  • 代碼配置;
  • xml配置;
  • 注解控制;
  • el表達(dá)式;
  • 自定義訪問控制器;

目前鑒權(quán)的需求比較簡單:登錄允許訪問,未登錄禁止訪問。因此可以定義了一個切面,控制所有需要安全控制的Controller。

spring security 提供了一些注解:

@PreAuthorize

控制一個方法是否能夠被調(diào)用,業(yè)務(wù)方法(HandlerMethod )的前置處理,比如:

@PreAuthorize("#id<10")限制只能查詢Id小于10的用戶

@PreAuthorize("principal.username.equals(#username)")限制只能查詢自己的信息

@PreAuthorize("#user.name.equals('abc')")限制只能新增用戶名稱為abc的用戶

@PostAuthorize

業(yè)務(wù)方法調(diào)用完之后進(jìn)行權(quán)限檢查,后置處理,比如:

@PostAuthorize("returnObject.id%2==0")

public User find(int id) {}

返回值的id是偶數(shù)則表示校驗通過,否則表示校驗失敗,將拋出AccessDeniedException

@PreFilter

對集合類型的參數(shù)進(jìn)行過濾,比如:

對集合ids中id不為偶數(shù)的進(jìn)行移除 @PreFilter(filterTarget="ids", value="filterObject%2==0") public void delete(List<Integer> ids, List<String> usernames) {}

@PostFilter

對集合類型的返回值進(jìn)行過濾,比如:

將對返回結(jié)果中id不為偶數(shù)的list中的對象進(jìn)行移除

@PostFilter("filterObject.id%2==0") public List<User> findAll() {}

@AuthenticationPrincipal 解決在業(yè)務(wù)方法內(nèi)對當(dāng)前用戶信息的方法
@Aspect
@Component
public class InControllerAspect {
    @Autowired
    BeforeInControllerMethods beforeInMethods;
 
    @Pointcut("execution(public * com.kxtx.oms.portal.controller.in.*.*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void hp() {
    };
 
    @Before("hp()")
    public void befor() {
        beforeInMethods.before();
    }
}
@Component
public class BeforeInControllerMethods {
    //@PreAuthorize("authenticated")要求所有訪問此方法的用戶必須登錄
    @PreAuthorize("authenticated")
    public void before() {
    }
}
//用戶信息獲取
@RequestMapping("/order/submit")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {
    // .. find messages for this user and return them ...
}

是不是有點復(fù)雜,復(fù)雜的是表現(xiàn)形式,實際上需要真正理解它的目的(為了要解決什么問題)。

參考資料

mvc-authentication-principal

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談SpringBoot集成Redis實現(xiàn)緩存處理(Spring AOP實現(xiàn))

    淺談SpringBoot集成Redis實現(xiàn)緩存處理(Spring AOP實現(xiàn))

    這篇文章主要介紹了淺談SpringBoot集成Redis實現(xiàn)緩存處理(Spring AOP實現(xiàn)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • Java方法及數(shù)組相關(guān)原理解析

    Java方法及數(shù)組相關(guān)原理解析

    這篇文章主要介紹了Java方法及數(shù)組相關(guān)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • Mybatis結(jié)果集映射一對多簡單入門教程

    Mybatis結(jié)果集映射一對多簡單入門教程

    本文給大家介紹Mybatis結(jié)果集映射一對多簡單入門教程,包括搭建數(shù)據(jù)庫環(huán)境的過程,idea搭建maven項目的代碼詳解,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-06-06
  • SpringBoot超詳細(xì)講解多數(shù)據(jù)源集成

    SpringBoot超詳細(xì)講解多數(shù)據(jù)源集成

    今天分享下SpringBoot多數(shù)據(jù)源集成,我怕麻煩,這里我覺得我的集成也應(yīng)該是最簡單的,清晰明了
    2022-05-05
  • 如何優(yōu)雅的拋出Spring Boot注解的異常詳解

    如何優(yōu)雅的拋出Spring Boot注解的異常詳解

    這篇文章主要給大家介紹了關(guān)于如何優(yōu)雅的拋出Spring Boot注解的異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • SpringBoot2實現(xiàn)MessageQueue消息隊列

    SpringBoot2實現(xiàn)MessageQueue消息隊列

    本文主要介紹了 SpringBoot2實現(xiàn)MessageQueue消息隊列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Java項目自動生成接口文檔教程

    Java項目自動生成接口文檔教程

    本文主要介紹了Java項目自動生成接口文檔教程,包含使用Apifox插件從IDEA生成的文檔,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • 在SpringBoot項目中的使用Swagger的方法示例

    在SpringBoot項目中的使用Swagger的方法示例

    這篇文章主要介紹了在SpringBoot項目中的使用Swagger的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Java 實現(xiàn)瀏覽器下載文件及文件預(yù)覽

    Java 實現(xiàn)瀏覽器下載文件及文件預(yù)覽

    這篇文章主要介紹了Java 實現(xiàn)瀏覽器下載文件及文件預(yù)覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 解析Java編程中對于包結(jié)構(gòu)的命名和訪問

    解析Java編程中對于包結(jié)構(gòu)的命名和訪問

    這篇文章主要介紹了Java編程中對于包結(jié)構(gòu)的命名和訪問,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-12-12

最新評論