中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Spring?Security怎么實現基于角色的訪問控制框架

發布時間:2023-05-05 17:38:13 來源:億速云 閱讀:236 作者:iii 欄目:開發技術

本篇內容介紹了“Spring Security怎么實現基于角色的訪問控制框架”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    說明

    Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。Spring Security是一個專注于為Java應用程序提供身份驗證和授權的框架。與所有Spring項目一樣,Spring安全性的真正威力在于它可以很容易地擴展以滿足定制需求。

    一般Web應用的需要進行認證和授權。

    • 用戶認證(Authentication):驗證當前訪問系統的是不是本系統的用戶,并且要確認具體是哪個用戶。

    • 用戶授權(Authorization):經過認證后判斷當前用戶是否有權限進行某個操作。在一個系統中,不同用戶所具有的權限是不同的。

    Spring Security與Shiro的區別

    Spring Security 是 Spring家族中的一個安全管理框架。相比與另外一個安全框架Shiro,它提供了更豐富的功能,社區資源也比Shiro豐富。相對于Shiro,在SSH/SSM中整合Spring Security都是比較麻煩的操作,但有了Spring boot之后,Spring Boot 對于 Spring Security 提供了 自動化配置方案,可以零配置使用 Spring Security。因此,一般來說常見的安全管理技術棧組合是:

    • SSM+Shiro

    • Spring Boot /Spring Clound +Spring Security

    簡單使用

    登錄校驗流程

    Spring?Security怎么實現基于角色的訪問控制框架

    引入Security

    在SpringBoot項目中使用SpringSecurity只需要引入依賴即可。

    <!-- spring security 安全認證 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-security</artifactId>
    		</dependency>

    設置用戶名和密碼

    在application.properties中添加屬性:

    server.port=8080
    #spring.security.user.name=root
    #spring.security.user.password=123456
    #mysql數據庫連接
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/security?serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=123456

    但是在application.properties中添加屬性意味著登錄系統的用戶名的密碼都是固定的,不推薦。可以使用配置類,在配置類中設置,配置類的優先級更高。

    使用配置類

    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter
    {
        /**
         * 自定義用戶認證邏輯
         */
        @Autowired
        private UserDetailsService userDetailsService;
        /**
         * 認證失敗處理類
         */
        @Autowired
        private AuthenticationEntryPointImpl unauthorizedHandler;
        /**
         * 退出處理類
         */
        @Autowired
        private LogoutSuccessHandlerImpl logoutSuccessHandler;
        /**
         * token認證過濾器
         */
        @Autowired
        private JwtAuthenticationTokenFilter authenticationTokenFilter;
        /**
         * 解決 無法直接注入 AuthenticationManager
         *
         * @return
         * @throws Exception
         */
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception
        {
            return super.authenticationManagerBean();
        }
        /**
         * anyRequest          |   匹配所有請求路徑
         * access              |   SpringEl表達式結果為true時可以訪問
         * anonymous           |   匿名可以訪問
         * denyAll             |   用戶不能訪問
         * fullyAuthenticated  |   用戶完全認證可以訪問(非remember-me下自動登錄)
         * hasAnyAuthority     |   如果有參數,參數表示權限,則其中任何一個權限可以訪問
         * hasAnyRole          |   如果有參數,參數表示角色,則其中任何一個角色可以訪問
         * hasAuthority        |   如果有參數,參數表示權限,則其權限可以訪問
         * hasIpAddress        |   如果有參數,參數表示IP地址,如果用戶IP和參數匹配,則可以訪問
         * hasRole             |   如果有參數,參數表示角色,則其角色可以訪問
         * permitAll           |   用戶可以任意訪問
         * rememberMe          |   允許通過remember-me登錄的用戶訪問
         * authenticated       |   用戶登錄后可訪問
         */
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception
        {
            httpSecurity
                    // CRSF禁用,因為不使用session
                    .csrf().disable()
                    // 認證失敗處理類
                    .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                    // 基于token,所以不需要session
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                    // 過濾請求
                    .authorizeRequests()
                    // 對于登錄login 允許匿名訪問
                    .antMatchers("/login").anonymous()
                    .antMatchers(
                            HttpMethod.GET,
                            "/*.html",
                            "/**/*.html",
                            "/**/*.css",
                            "/file/**",
                            "/**/*.js"
                    ).permitAll()
                    // 除上面外的所有請求全部需要鑒權認證
                    .anyRequest().authenticated()
                    .and()
                    .headers().frameOptions().disable();
            httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
            // 添加JWT filter
            httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        }
        /**
         * 強散列哈希加密實現
         */
        @Bean
        public BCryptPasswordEncoder bCryptPasswordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
        /**
         * 身份認證接口
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception
        {
            auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
        }
    }
    • @EnableGlobalMethodSecurity :當我們想要開啟spring方法級安全時,只需要在任何 @Configuration實例上使用 @EnableGlobalMethodSecurity 注解就能達到此目的。

    • prePostEnabled = true 會解鎖 @PreAuthorize 和 @PostAuthorize 兩個注解。從名字就可以看出@PreAuthorize 注解會在方法執行前進行驗證,而 @PostAuthorize 注解會在方法執行后進行驗證。

    /**
     * 自定義權限實現,ss取自SpringSecurity首字母
     */
    @Service("ss")
    public class PermissionService
    {
        /** 所有權限標識 */
        private static final String ALL_PERMISSION = "*:*:*";
        /** 管理員角色權限標識 */
        private static final String SUPER_ADMIN = "admin";
        private static final String ROLE_DELIMETER = ",";
        private static final String PERMISSION_DELIMETER = ",";
        @Autowired
        private TokenService tokenService;
        /**
         * 驗證用戶是否具備某權限
         *
         * @param permission 權限字符串
         * @return 用戶是否具備某權限
         */
        public boolean hasPermi(String permission)
        {
            if (StringUtils.isEmpty(permission))
            {
                return false;
            }
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
            {
                return false;
            }
            return hasPermissions(loginUser.getPermissions(), permission);
        }
        /**
         * 驗證用戶是否不具備某權限,與 hasPermi邏輯相反
         *
         * @param permission 權限字符串
         * @return 用戶是否不具備某權限
         */
        public boolean lacksPermi(String permission)
        {
            return hasPermi(permission) != true;
        }
        /**
         * 驗證用戶是否具有以下任意一個權限
         *
         * @param permissions 以 PERMISSION_NAMES_DELIMETER 為分隔符的權限列表
         * @return 用戶是否具有以下任意一個權限
         */
        public boolean hasAnyPermi(String permissions)
        {
            if (StringUtils.isEmpty(permissions))
            {
                return false;
            }
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
            {
                return false;
            }
            Set<String> authorities = loginUser.getPermissions();
            for (String permission : permissions.split(PERMISSION_DELIMETER))
            {
                if (permission != null && hasPermissions(authorities, permission))
                {
                    return true;
                }
            }
            return false;
        }
        /**
         * 判斷用戶是否擁有某個角色
         *
         * @param role 角色字符串
         * @return 用戶是否具備某角色
         */
        public boolean hasRole(String role)
        {
            if (StringUtils.isEmpty(role))
            {
                return false;
            }
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
            {
                return false;
            }
            for (SysRole sysRole : loginUser.getUser().getRoles())
            {
                String roleKey = sysRole.getRoleKey();
                if (SUPER_ADMIN.contains(roleKey) || roleKey.contains(StringUtils.trim(role)))
                {
                    return true;
                }
            }
            return false;
        }
        /**
         * 驗證用戶是否不具備某角色,與 isRole邏輯相反。
         *
         * @param role 角色名稱
         * @return 用戶是否不具備某角色
         */
        public boolean lacksRole(String role)
        {
            return hasRole(role) != true;
        }
        /**
         * 驗證用戶是否具有以下任意一個角色
         *
         * @param roles 以 ROLE_NAMES_DELIMETER 為分隔符的角色列表
         * @return 用戶是否具有以下任意一個角色
         */
        public boolean hasAnyRoles(String roles)
        {
            if (StringUtils.isEmpty(roles))
            {
                return false;
            }
            LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
            if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
            {
                return false;
            }
            for (String role : roles.split(ROLE_DELIMETER))
            {
                if (hasRole(role))
                {
                    return true;
                }
            }
            return false;
        }
        /**
         * 判斷是否包含權限
         *
         * @param permissions 權限列表
         * @param permission 權限字符串
         * @return 用戶是否具備某權限
         */
        private boolean hasPermissions(Set<String> permissions, String permission)
        {
            return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
        }
    }
        /**
         * 獲取用戶列表
         */
        @PreAuthorize("@ss.hasPermi('system:user:list')")
        @GetMapping("/list")
        public TableDataInfo list(SysUser user)
        {
            startPage();
            List<SysUser> list = userService.selectUserList(user);
            return getDataTable(list);
        }
    • antMatchers():可傳入多個String參數,用于匹配多個請求API。結合permitAll(),可同時對多個請求設置忽略認證。

    • and():用于表示上一項配置已經結束,下一項配置將開始。

    創建一個Service文件用于Security查詢用戶信息:

    @Service
    public class UserDetailsServiceImpl implements UserDetailsService
    {
        private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
        @Autowired
        private ISysUserService userService;
        @Autowired
        private SysPermissionService permissionService;
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
        {
        	// 根據賬戶號查詢用戶信息
            SysUser user = userService.selectUserByUserName(username);
            if (StringUtils.isNull(user))
            {
                log.info("登錄用戶:{} 不存在.", username);
                throw new UsernameNotFoundException("登錄用戶:" + username + " 不存在");
            }
            else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
            {
                log.info("登錄用戶:{} 已被刪除.", username);
                throw new BaseException("對不起,您的賬號:" + username + " 已被刪除");
            }
            else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
            {
                log.info("登錄用戶:{} 已被停用.", username);
                throw new BaseException("對不起,您的賬號:" + username + " 已停用");
            }
            return createLoginUser(user);
        }
        public UserDetails createLoginUser(SysUser user)
        {
        	// 獲取用戶權限
            return new LoginUser(user, permissionService.getMenuPermission(user));
        }
    }

    當用戶請求時,Security便會攔截請求,取出其中的username字段,從Service中查詢該賬戶,并將信息填充到一個userDetails對象中返回。這樣Security便拿到了填充后的userDetails對象,也就是獲取到了包括權限在內的用戶信息。

    過濾規則

    WebSecurityConfigurerAdapter.configure(HttpSecurity http)

    授權方式

    Security的授權方式有兩種:

    • WEB授權(基于請求URL),在configure(HttpSecurity httpSecurity)中設置。

    • 方法授權(在方法上使用注解授權)

    WEB授權
    @Override
    httpSecurity.authorizeRequests() // 對請求進行授權
        .antMatchers("/test1").hasAuthority("p1") // 設置/test1接口需要p1權限
        .antMatchers("/test2").hasAnyRole("admin", "manager") // 設置/test2接口需要admin或manager角色
        .and()
        .headers().frameOptions().disable();

    其中:

    • hasAuthority(“p1”)表示需要p1權限

    • hasAnyAuthority(“p1”, “p2”)表示p1或p2權限皆可

    同理:

    • hasRole(“p1”)表示需要p1角色

    • hasAnyRole(“p1”, “p2”)表示p1或p2角色皆可

    方法授權
        /**
         * 獲取用戶列表
         */
        @PreAuthorize("@ss.hasPermi('system:user:list')")
        @GetMapping("/list")
        public TableDataInfo list(SysUser user)
        {
            startPage();
            List<SysUser> list = userService.selectUserList(user);
            return getDataTable(list);
        }

    順序優先級

    校驗是按照配置的順序從上往下進行。若某個條件已匹配過,則后續同條件的匹配將不再被執行。也就是說,若多條規則是相互矛盾的,則只有第一條規則生效。典型地:

    // 只有具有權限a的用戶才能訪問/get/resource
    httpSecurity.authorizeRequests()
        .antMatchers("/get/resource").hasAuthority("a")
        .anyRequest().permitAll();
    // 所有用戶都能訪問/get/resource
    httpSecurity.authorizeRequests()
        .anyRequest().permitAll()
        .antMatchers("/get/resource").hasAuthority("a");

    但若是包含關系,則需要將細粒度的規則放在前面:

    // /get/resource需要權限a,除此之外所有的/get/**都可以隨意訪問
    httpSecurity.authorizeRequests()
        .antMatchers("/get/resource").hasAuthority("a")
        .antMatchers("/get/**").permitAll();

    登出

    httpSecurity.logoutUrl()和httpSecurity.addLogoutHandler()是沖突的,通常只用其中之一。對于使用token且服務端不進行存儲的情況,不需要請求服務端進行登出,直接由前端將token丟棄即可。

    httpSecurity.logout().logoutUrl(“/logout”).logoutSuccessHandler(logoutSuccessHandler);

    /**
     * 自定義退出處理類 返回成功
     */
    @Configuration
    public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
    {
        @Autowired
        private TokenService tokenService;
        /**
         * 退出處理
         * 
         * @return
         */
        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException
        {
            LoginUser loginUser = tokenService.getLoginUser(request);
            if (StringUtils.isNotNull(loginUser))
            {
                String userName = loginUser.getUsername();
                // 刪除用戶緩存記錄
                tokenService.delLoginUser(loginUser.getToken());
            }
            ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));
        }
    }

    跨域

    httpSecurity.cors()
    	.and()
    	.csrf().disable();

    cors()允許跨域資源訪問。

    csrf().disable()禁用跨域安全驗證。

    認證失敗處理類

    /**
     * 認證失敗處理類 返回未授權
     */
    @Component
    public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
    {
        private static final long serialVersionUID = -8970718410437077606L;
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
                throws IOException
        {
            int code = HttpStatus.UNAUTHORIZED;
            String msg = StringUtils.format("請求訪問:{},認證失敗,無法訪問系統資源", request.getRequestURI());
            ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
        }
    }

    “Spring Security怎么實現基于角色的訪問控制框架”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    清苑县| 正安县| 依兰县| 江津市| 榕江县| 烟台市| 辉南县| 尼勒克县| 称多县| 通江县| 驻马店市| 孝义市| 德令哈市| 吴江市| 福泉市| 泽州县| 日土县| 香河县| 邹城市| 长海县| 齐齐哈尔市| 潼关县| 广宗县| 灌南县| 静安区| 扬州市| 昌平区| 竹北市| 钟山县| 安吉县| 西乌珠穆沁旗| 茌平县| 和龙市| 葫芦岛市| 汽车| 天等县| 湘乡市| 阳曲县| 罗定市| 满城县| 惠安县|