溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring Security 動態(tài)url權(quán)限控制(三)

發(fā)布時間:2020-07-01 18:55:03 來源:網(wǎng)絡(luò) 閱讀:722 作者:鄭小清 欄目:編程語言

一、前言

本篇文章將講述Spring Security 動態(tài)分配url權(quán)限,未登錄權(quán)限控制,登錄過后根據(jù)登錄用戶角色授予訪問url權(quán)限

基本環(huán)境
  1. spring-boot 2.1.8
  2. mybatis-plus 2.2.0
  3. mysql 數(shù)據(jù)庫
  4. maven項(xiàng)目
Spring Security入門學(xué)習(xí)可參考之前文章:
  1. SpringBoot集成Spring Security入門體驗(yàn)(一)
    https://blog.csdn.net/qq_38225558/article/details/101754743
  2. Spring Security 自定義登錄認(rèn)證(二)
    https://blog.csdn.net/qq_38225558/article/details/102542072

二、數(shù)據(jù)庫建表

Spring Security 動態(tài)url權(quán)限控制(三)

表關(guān)系簡介:
  1. 用戶表t_sys_user 關(guān)聯(lián) 角色表t_sys_role 兩者建立中間關(guān)系表t_sys_user_role
  2. 角色表t_sys_role 關(guān)聯(lián) 權(quán)限表t_sys_permission 兩者建立中間關(guān)系表t_sys_role_permission
  3. 最終體現(xiàn)效果為當(dāng)前登錄用戶所具備的角色關(guān)聯(lián)能訪問的所有url,只要給角色分配相應(yīng)的url權(quán)限即可

溫馨小提示:這里邏輯根據(jù)個人業(yè)務(wù)來定義,小編這里講解案例只給用戶對應(yīng)的角色分配訪問權(quán)限,像其它的 直接給用戶分配權(quán)限等等可以自己實(shí)現(xiàn)

表模擬數(shù)據(jù)如下:

Spring Security 動態(tài)url權(quán)限控制(三)

三、Spring Security 動態(tài)權(quán)限控制

1、未登錄訪問權(quán)限控制

自定義AdminAuthenticationEntryPoint類實(shí)現(xiàn)AuthenticationEntryPoint

這里是認(rèn)證權(quán)限入口 -> 即在未登錄的情況下訪問所有接口都會攔截到此(除了放行忽略接口)

溫馨小提示ResponseUtilsApiResult是小編這里模擬前后端分離情況下返回json格式數(shù)據(jù)所使用工具類,具體實(shí)現(xiàn)可參考文末給出的demo源碼

@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
        ResponseUtils.out(response, ApiResult.fail("未登錄!?。?));
    }
}
2、自定義過濾器MyAuthenticationFilter繼承OncePerRequestFilter實(shí)現(xiàn)訪問鑒權(quán)

每次訪問接口都會經(jīng)過此,我們可以在這里記錄請求參數(shù)、響應(yīng)內(nèi)容,或者處理前后端分離情況下,以token換用戶權(quán)限信息,token是否過期,請求頭類型是否正確,防止非法請求等等

  1. logRequestBody()方法:記錄請求消息體
  2. logResponseBody()方法:記錄響應(yīng)消息體

【注:請求的HttpServletRequest流只能讀一次,下一次就不能讀取了,因此這里要使用自定義的MultiReadHttpServletRequest工具解決流只能讀一次的問題,響應(yīng)同理,具體可參考文末demo源碼實(shí)現(xiàn)】

@Slf4j
@Component
public class MyAuthenticationFilter extends OncePerRequestFilter {

    private final UserDetailsServiceImpl userDetailsService;

    protected MyAuthenticationFilter(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("請求頭類型: " + request.getContentType());
        if ((request.getContentType() == null && request.getContentLength() > 0) || (request.getContentType() != null && !request.getContentType().contains(Constants.REQUEST_HEADERS_CONTENT_TYPE))) {
            filterChain.doFilter(request, response);
            return;
        }

        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(request);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(response);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            // 記錄請求的消息體
            logRequestBody(wrappedRequest);

//            String token = "123";
            // 前后端分離情況下,前端登錄后將token儲存在cookie中,每次訪問接口時通過token去拿用戶權(quán)限
            String token = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
            log.debug("后臺檢查令牌:{}", token);
            if (StringUtils.isNotBlank(token)) {
                // 檢查token
                SecurityUser securityUser = userDetailsService.getUserByToken(token);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    throw new AccessDeniedException("TOKEN已過期,請重新登錄!");
                }
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
                // 全局注入角色權(quán)限信息和登錄用戶基本信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } finally {
            stopWatch.stop();
            long usedTimes = stopWatch.getTotalTimeMillis();
            // 記錄響應(yīng)的消息體
            logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
        }

    }

    private String logRequestBody(MultiReadHttpServletRequest request) {
        MultiReadHttpServletRequest wrapper = request;
        if (wrapper != null) {
            try {
                String bodyJson = wrapper.getBodyJsonStrByJson(request);
                String url = wrapper.getRequestURI().replace("http://", "/");
                System.out.println("-------------------------------- 請求url: " + url + " --------------------------------");
                Constants.URL_MAPPING_MAP.put(url, url);
                log.info("`{}` 接收到的參數(shù): {}",url , bodyJson);
                return bodyJson;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void logResponseBody(MultiReadHttpServletRequest request, MultiReadHttpServletResponse response, long useTime) {
        MultiReadHttpServletResponse wrapper = response;
        if (wrapper != null) {
            byte[] buf = wrapper.getBody();
            if (buf.length > 0) {
                String payload;
                try {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException ex) {
                    payload = "[unknown]";
                }
                log.info("`{}`  耗時:{}ms  返回的參數(shù): {}", Constants.URL_MAPPING_MAP.get(request.getRequestURI()), useTime, payload);
            }
        }
    }

}
3、自定義UserDetailsServiceImpl實(shí)現(xiàn)UserDetailsService 和 自定義SecurityUser實(shí)現(xiàn)UserDetails 認(rèn)證用戶詳情

這個在上一篇文章中也提及過,但上次未做角色權(quán)限處理,這次我們來一起加上吧

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;

    /***
     * 根據(jù)賬號獲取用戶信息
     * @param username:
     * @return: org.springframework.security.core.userdetails.UserDetails
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 從數(shù)據(jù)庫中取出用戶信息
        List<User> userList = userMapper.selectList(new EntityWrapper<User>().eq("username", username));
        User user;
        // 判斷用戶是否存在
        if (!CollectionUtils.isEmpty(userList)) {
            user = userList.get(0);
        } else {
            throw new UsernameNotFoundException("用戶名不存在!");
        }
        // 返回UserDetails實(shí)現(xiàn)類
        return new SecurityUser(user, getUserRoles(user.getId()));
    }

    /***
     * 根據(jù)token獲取用戶權(quán)限與基本信息
     *
     * @param token:
     * @return: com.zhengqing.config.security.dto.SecurityUser
     */
    public SecurityUser getUserByToken(String token) {
        User user = null;
        List<User> loginList = userMapper.selectList(new EntityWrapper<User>().eq("token", token));
        if (!CollectionUtils.isEmpty(loginList)) {
            user = loginList.get(0);
        }
        return user != null ? new SecurityUser(user, getUserRoles(user.getId())) : null;
    }

    /**
     * 根據(jù)用戶id獲取角色權(quán)限信息
     *
     * @param userId
     * @return
     */
    private List<Role> getUserRoles(Integer userId) {
        List<UserRole> userRoles = userRoleMapper.selectList(new EntityWrapper<UserRole>().eq("user_id", userId));
        List<Role> roleList = new LinkedList<>();
        for (UserRole userRole : userRoles) {
            Role role = roleMapper.selectById(userRole.getRoleId());
            roleList.add(role);
        }
        return roleList;
    }

}

這里再說下自定義SecurityUser是因?yàn)镾pring Security自帶的 UserDetails (存儲當(dāng)前用戶基本信息) 有時候可能不滿足我們的需求,因此我們可以自己定義一個來擴(kuò)展我們的需求
Spring Security 動態(tài)url權(quán)限控制(三)
getAuthorities()方法:即授予當(dāng)前用戶角色權(quán)限信息

@Data
@Slf4j
public class SecurityUser implements UserDetails {
    /**
     * 當(dāng)前登錄用戶
     */
    private transient User currentUserInfo;
    /**
     * 角色
     */
    private transient List<Role> roleList;

    public SecurityUser() { }

    public SecurityUser(User user) {
        if (user != null) {
            this.currentUserInfo = user;
        }
    }

    public SecurityUser(User user, List<Role> roleList) {
        if (user != null) {
            this.currentUserInfo = user;
            this.roleList = roleList;
        }
    }

    /**
     * 獲取當(dāng)前用戶所具有的角色
     *
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(this.roleList)) {
            for (Role role : this.roleList) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getCode());
                authorities.add(authority);
            }
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
4、自定義UrlFilterInvocationSecurityMetadataSource實(shí)現(xiàn)FilterInvocationSecurityMetadataSource重寫getAttributes()方法 獲取訪問該url所需要的角色權(quán)限信息

執(zhí)行完之后到 下一步 UrlAccessDecisionManager 中認(rèn)證權(quán)限

@Component
public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Autowired
    PermissionMapper permissionMapper;
    @Autowired
    RolePermissionMapper rolePermissionMapper;
    @Autowired
    RoleMapper roleMapper;

    /***
     * 返回該url所需要的用戶權(quán)限信息
     *
     * @param object: 儲存請求url信息
     * @return: null:標(biāo)識不需要任何權(quán)限都可以訪問
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 獲取當(dāng)前請求url
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        // TODO 忽略url請放在此處進(jìn)行過濾放行
        if ("/login".equals(requestUrl) || requestUrl.contains("logout")) {
            return null;
        }

        // 數(shù)據(jù)庫中所有url
        List<Permission> permissionList = permissionMapper.selectList(null);
        for (Permission permission : permissionList) {
            // 獲取該url所對應(yīng)的權(quán)限
            if (requestUrl.equals(permission.getUrl())) {
                List<RoleMenu> permissions = rolePermissionMapper.selectList(new EntityWrapper<RoleMenu>().eq("permission_id", permission.getId()));
                List<String> roles = new LinkedList<>();
                if (!CollectionUtils.isEmpty(permissions)){
                    Integer roleId = permissions.get(0).getRoleId();
                    Role role = roleMapper.selectById(roleId);
                    roles.add(role.getCode());
                }
                // 保存該url對應(yīng)角色權(quán)限信息
                return SecurityConfig.createList(roles.toArray(new String[roles.size()]));
            }
        }
        // 如果數(shù)據(jù)中沒有找到相應(yīng)url資源則為非法訪問,要求用戶登錄再進(jìn)行操作
        return SecurityConfig.createList(Constants.ROLE_LOGIN);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return FilterInvocation.class.isAssignableFrom(aClass);
    }
}
5、自定義UrlAccessDecisionManager實(shí)現(xiàn)AccessDecisionManager重寫decide()方法 對訪問url進(jìn)行權(quán)限認(rèn)證處理

此處小編的處理邏輯是只要包含其中一個角色即可訪問

@Component
public class UrlAccessDecisionManager implements AccessDecisionManager {

    /**
     * @param authentication: 當(dāng)前登錄用戶的角色信息
     * @param object: 請求url信息
     * @param collection: `UrlFilterInvocationSecurityMetadataSource`中的getAttributes方法傳來的,表示當(dāng)前請求需要的角色(可能有多個)
     * @return: void
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException {
        // 遍歷角色
        for (ConfigAttribute ca : collection) {
            // ① 當(dāng)前url請求需要的權(quán)限
            String needRole = ca.getAttribute();
            if (Constants.ROLE_LOGIN.equals(needRole)) {
                if (authentication instanceof AnonymousAuthenticationToken) {
                    throw new BadCredentialsException("未登錄!");
                } else {
                    throw new AccessDeniedException("未授權(quán)該url!");
                }
            }

            // ② 當(dāng)前用戶所具有的角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                // 只要包含其中一個角色即可訪問
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("請聯(lián)系管理員分配權(quán)限!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
6、自定義無權(quán)限處理器 UrlAccessDeniedHandler實(shí)現(xiàn)AccessDeniedHandler重寫handle()方法

在這里自定義403無權(quán)限響應(yīng)內(nèi)容,登錄過后的權(quán)限處理
:要和未登錄時的權(quán)限處理區(qū)分開哦~ 】

@Component
public class UrlAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        ResponseUtils.out(response, ApiResult.fail(403, e.getMessage()));
    }
}
7、最后在Security 核心配置類中配置以上處理
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 訪問鑒權(quán) - 認(rèn)證token、簽名...
     */
    private final MyAuthenticationFilter myAuthenticationFilter;
    /**
     * 訪問權(quán)限認(rèn)證異常處理
     */
    private final AdminAuthenticationEntryPoint adminAuthenticationEntryPoint;
    /**
     * 用戶密碼校驗(yàn)過濾器
     */
    private final AdminAuthenticationProcessingFilter adminAuthenticationProcessingFilter;

    // 上面是登錄認(rèn)證相關(guān)  下面為url權(quán)限相關(guān) - ========================================================================================

    /**
     * 獲取訪問url所需要的角色信息
     */
    private final UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
    /**
     * 認(rèn)證權(quán)限處理 - 將上面所獲得角色權(quán)限與當(dāng)前登錄用戶的角色做對比,如果包含其中一個角色即可正常訪問
     */
    private final UrlAccessDecisionManager urlAccessDecisionManager;
    /**
     * 自定義訪問無權(quán)限接口時403響應(yīng)內(nèi)容
     */
    private final UrlAccessDeniedHandler urlAccessDeniedHandler;

    public SecurityConfig(MyAuthenticationFilter myAuthenticationFilter, AdminAuthenticationEntryPoint adminAuthenticationEntryPoint, AdminAuthenticationProcessingFilter adminAuthenticationProcessingFilter, UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource, UrlAccessDeniedHandler urlAccessDeniedHandler, UrlAccessDecisionManager urlAccessDecisionManager) {
        this.myAuthenticationFilter = myAuthenticationFilter;
        this.adminAuthenticationEntryPoint = adminAuthenticationEntryPoint;
        this.adminAuthenticationProcessingFilter = adminAuthenticationProcessingFilter;
        this.urlFilterInvocationSecurityMetadataSource = urlFilterInvocationSecurityMetadataSource;
        this.urlAccessDeniedHandler = urlAccessDeniedHandler;
        this.urlAccessDecisionManager = urlAccessDecisionManager;
    }

    /**
     * 權(quán)限配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();

        // 禁用CSRF 開啟跨域
        http.csrf().disable().cors();

        // 未登錄認(rèn)證異常
        http.exceptionHandling().authenticationEntryPoint(adminAuthenticationEntryPoint);
        // 登錄過后訪問無權(quán)限的接口時自定義403響應(yīng)內(nèi)容
        http.exceptionHandling().accessDeniedHandler(urlAccessDeniedHandler);

        // url權(quán)限認(rèn)證處理
        registry.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                o.setAccessDecisionManager(urlAccessDecisionManager);
                return o;
            }
        });

        // 不創(chuàng)建會話 - 即通過前端傳token到后臺過濾器中驗(yàn)證是否存在訪問權(quán)限
//        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 標(biāo)識訪問 `/home` 這個接口,需要具備`ADMIN`角色
//        registry.antMatchers("/home").hasRole("ADMIN");
        // 標(biāo)識只能在 服務(wù)器本地ip[127.0.0.1或localhost] 訪問 `/home` 這個接口,其他ip地址無法訪問
        registry.antMatchers("/home").hasIpAddress("127.0.0.1");
        // 允許匿名的url - 可理解為放行接口 - 多個接口使用,分割
        registry.antMatchers("/login", "/index").permitAll();
//        registry.antMatchers("/**").access("hasAuthority('admin')");
        // OPTIONS(選項(xiàng)):查找適用于一個特定網(wǎng)址資源的通訊選擇。 在不需執(zhí)行具體的涉及數(shù)據(jù)傳輸?shù)膭幼髑闆r下, 允許客戶端來確定與資源相關(guān)的選項(xiàng)以及 / 或者要求, 或是一個服務(wù)器的性能
        registry.antMatchers(HttpMethod.OPTIONS, "/**").denyAll();
        // 自動登錄 - cookie儲存方式
        registry.and().rememberMe();
        // 其余所有請求都需要認(rèn)證
        registry.anyRequest().authenticated();
        // 防止iframe 造成跨域
        registry.and().headers().frameOptions().disable();

        // 自定義過濾器在登錄時認(rèn)證用戶名、密碼
        http.addFilterAt(adminAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(myAuthenticationFilter, BasicAuthenticationFilter.class);
    }

    /**
     * 忽略攔截url或靜態(tài)資源文件夾 - web.ignoring(): 會直接過濾該url - 將不會經(jīng)過Spring Security過濾器鏈
     *                             http.permitAll(): 不會繞開springsecurity驗(yàn)證,相當(dāng)于是允許該路徑通過
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(HttpMethod.GET,
                "/favicon.ico",
                "/*.html",
                "/**/*.css",
                "/**/*.js");
    }

}

四、編寫測試代碼

控制層:

@Slf4j
@RestController
public class IndexController {

    @GetMapping("/")
    public ModelAndView showHome() {
        return new ModelAndView("home.html");
    }

    @GetMapping("/index")
    public String index() {
        return "Hello World ~";
    }

    @GetMapping("/login")
    public ModelAndView login() {
        return new ModelAndView("login.html");
    }

    @GetMapping("/home")
    public String home() {
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        log.info("登陸人:" + name);
        return "Hello~ " + name;
    }

    @GetMapping(value ="/admin")
    // 訪問路徑`/admin` 具有`ADMIN`角色權(quán)限   【這種是寫死方式】
//    @PreAuthorize("hasPermission('/admin','ADMIN')")
    public String admin() {
        return "Hello~ 管理員";
    }

    @GetMapping("/test")
    public String test() {
        return "Hello~ 測試權(quán)限訪問接口";
    }

}

頁面和其它相關(guān)代碼這里就不貼出來了,具體可參考文末demo源碼

五、運(yùn)行訪問測試效果

1、未登錄時

Spring Security 動態(tài)url權(quán)限控制(三)

2、登錄過后如果有權(quán)限則正常訪問

Spring Security 動態(tài)url權(quán)限控制(三)

3、登錄過后,沒有權(quán)限

這里我們可以修改數(shù)據(jù)庫角色權(quán)限關(guān)聯(lián)表t_sys_role_permission來進(jìn)行測試哦 ~

Security 動態(tài)url權(quán)限也就是依賴這張表來判斷的,只要修改這張表分配角色對應(yīng)url權(quán)限資源,用戶訪問url時就會動態(tài)的去判斷,無需做其他處理,如果是將權(quán)限信息放在了緩存中,修改表數(shù)據(jù)時及時更新緩存即可!
Spring Security 動態(tài)url權(quán)限控制(三)

Spring Security 動態(tài)url權(quán)限控制(三)

4、登錄過后,訪問數(shù)據(jù)庫中沒有配置的url 并且 在Security中沒有忽略攔截的url時

Spring Security 動態(tài)url權(quán)限控制(三)

六、總結(jié)

  1. 自定義未登錄權(quán)限處理器AdminAuthenticationEntryPoint - 自定義未登錄時訪問無權(quán)限url響應(yīng)內(nèi)容
  2. 自定義訪問鑒權(quán)過濾器MyAuthenticationFilter - 記錄請求響應(yīng)日志、是否合法訪問,驗(yàn)證token過期等
  3. 自定義UrlFilterInvocationSecurityMetadataSource - 獲取訪問該url所需要的角色權(quán)限
  4. 自定義UrlAccessDecisionManager - 對訪問url進(jìn)行權(quán)限認(rèn)證處理
  5. 自定義UrlAccessDeniedHandler - 登錄過后訪問無權(quán)限url失敗處理器 - 自定義403無權(quán)限響應(yīng)內(nèi)容
  6. Security核心配置類中配置以上處理器和過濾器
Security動態(tài)權(quán)限相關(guān)代碼:

Spring Security 動態(tài)url權(quán)限控制(三)

本文案例demo源碼

https://gitee.com/zhengqingya/java-workspace

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI