天天看點

SpringSecurity+JWT項目實戰之Java權限管理實戰(五)--通路控制

前言

本文是參考尚學堂SpringSecurity精講,僅作為學習記錄使用。

這個系列設計到的技術點如下:

  • SpringSecurity
  • Oauth2
  • SpringSecurity + Oauth2
  • SpringSecurity +JWT
  • SpringSecurity + Oauth2
  • SpringSecurity + Oauth2 + JWT

背景

之前學習的登入使用者權限判斷實際上底層實作都是調用access(表達式)。現在我們來看一下這個權限控制底層的邏輯。

  • access()方法使用

    可以通過 access() 實作和之前學習的權限控制完成相同的功能。以 hasRole 和 和 permitAll 舉例

    SpringSecurity+JWT項目實戰之Java權限管理實戰(五)--通路控制
    SpringSecurity+JWT項目實戰之Java權限管理實戰(五)--通路控制
  • 使用自定義方法

    雖然這裡面已經包含了很多的表達式(方法)但是在實際項目中很有可能出現需要自己自定義邏輯的情況。判斷登入使用者是否具有通路目前 URL 權限。

  • 建立接口及實作類
  • MyService
package com.mjdai.springsecurity;

import org.springframework.security.core.Authentication;

import javax.servlet.http.HttpServletRequest;

public interface MyService {
    boolean hasPermission(HttpServletRequest request, Authentication authentication);
}

           
  • MyServiceImpl
package com.mjdai.springsecurity.service;

import com.mjdai.springsecurity.MyService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

public class MyServiceImpl implements MyService {
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication
            authentication) {
        Object obj = authentication.getPrincipal();
        if (obj instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) obj;
            Collection<? extends GrantedAuthority> authorities =
                    userDetails.getAuthorities();
            return authorities.contains(new
                    SimpleGrantedAuthority(request.getRequestURI()));
        }
        return false;
    }
}

           
  • 修改配置類
http.authorizeRequests()
                // login.html不需要被認證
//                .antMatchers("/login.html").permitAll()
                .antMatchers("/login.html").access("permitAll")
                // error.html不需要被認證
                .antMatchers("/error.html").permitAll()
                // 所有請求都必須被認證,必須登入後被通路
//                .antMatchers("/main.html").hasAuthority("admin")
//                .antMatchers("/main.html").hasRole("abd")
                .antMatchers("/main.html").access("hasRole(\"abd\")")
                .anyRequest().access("@myServiceImpl.hasPermission(request,authentication)")
                .anyRequest().authenticated();
           

基于注解的通路控制

在 Spring Security 中提供了一些通路控制的注解。這些注解都是預設是都不可用的,需要通過@EnableGlobalMethodSecurity 進行開啟後使用。如果設定的條件允許,程式正常執行。如果不允許會報 500,不允許通路。

org.springframework.security.access.AccessDeniedException

這些注解可以寫到 Service 接口或方法上,也可以寫到 Controller或 Controller 的方法上。通常情況下都是寫在控制器方法上的,控制接口URL是否允許被通路。

  • @Secured

    @Secured 是專門用于判斷是否具有角色的。能寫在方法或類上。參數要以 ROLE_開頭。

package org.springframework.security.access.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Secured {
	/**
	 * Returns the list of security configuration attributes (e.g.&nbsp;ROLE_USER, ROLE_ADMIN).
	 *
	 * @return String[] The secure method attributes
	 */
	String[] value();
}
           
  • 開啟注解

    在 啟 動 類 ( 也 可 以 在 配 置 類 等 能 夠 掃 描 的 類 上 ) 上 添 加

    @EnableGlobalMethodSecurity(securedEnabled = true)

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class MjdaiSpringSecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(MjdaiSpringSecurityApplication.class, args);
    }
}
           

在控制器方法上添加@Secured 注解

@Secured("ROLE_abc")
 @RequestMapping(value = "/toMain",method = RequestMethod.POST)
 public String toMain(){
        System.out.println("到這裡了嗎?");
        return "redirect:main.html";
 }
           
  • @PreAuthorize/@PostAuthorize

    @PreAuthorize 和@PostAuthorize 都是方法或類級别注解。

  1. @PreAuthorize 表示通路方法或類在執行之前先判斷權限,大多情況下都是使用這個注解,注解的參數和access()方法參數取值相同,都是權限表達式。
  2. @PostAuthorize 表示方法或類執行結束後判斷權限,此注解很少被使用到。
  • 開啟注解

    @EnableGlobalMethodSecurity(prePostEnabled = true)

  • 添加@PreAuthorize

    在控制器方法上添加@PreAuthorize,參數可以是任何 access()支援的表達式

@PreAuthorize("hasRole('ROLE_abc')")
@RequestMapping("/toMain")