最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Security實(shí)現(xiàn)基于角色的訪問控制框架

 更新時(shí)間:2023年04月23日 08:56:06   作者:Wen先森  
Spring Security是一個(gè)功能強(qiáng)大的安全框架,提供了基于角色的訪問控制、身份驗(yàn)證、授權(quán)等安全功能,可輕松保護(hù)Web應(yīng)用程序的安全,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧

說(shuō)明

Spring Security是一個(gè)功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架。Spring Security是一個(gè)專注于為Java應(yīng)用程序提供身份驗(yàn)證和授權(quán)的框架。與所有Spring項(xiàng)目一樣,Spring安全性的真正威力在于它可以很容易地?cái)U(kuò)展以滿足定制需求。

一般Web應(yīng)用的需要進(jìn)行認(rèn)證和授權(quán)。

  • 用戶認(rèn)證(Authentication):驗(yàn)證當(dāng)前訪問系統(tǒng)的是不是本系統(tǒng)的用戶,并且要確認(rèn)具體是哪個(gè)用戶。
  • 用戶授權(quán)(Authorization):經(jīng)過(guò)認(rèn)證后判斷當(dāng)前用戶是否有權(quán)限進(jìn)行某個(gè)操作。在一個(gè)系統(tǒng)中,不同用戶所具有的權(quán)限是不同的。

Spring Security與Shiro的區(qū)別

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

  • SSM+Shiro
  • Spring Boot /Spring Clound +Spring Security

簡(jiǎn)單使用

登錄校驗(yàn)流程

引入Security

在SpringBoot項(xiàng)目中使用SpringSecurity只需要引入依賴即可。

<!-- spring security 安全認(rèn)證 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

設(shè)置用戶名和密碼

在application.properties中添加屬性:

server.port=8080
#spring.security.user.name=root
#spring.security.user.password=123456
#mysql數(shù)據(jù)庫(kù)連接
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中添加屬性意味著登錄系統(tǒng)的用戶名的密碼都是固定的,不推薦??梢允褂门渲妙悾谂渲妙愔性O(shè)置,配置類的優(yōu)先級(jí)更高。

使用配置類

@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * 自定義用戶認(rèn)證邏輯
     */
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 認(rèn)證失敗處理類
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;
    /**
     * 退出處理類
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;
    /**
     * token認(rèn)證過(guò)濾器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;
    /**
     * 解決 無(wú)法直接注入 AuthenticationManager
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception
    {
        return super.authenticationManagerBean();
    }
    /**
     * anyRequest          |   匹配所有請(qǐng)求路徑
     * access              |   SpringEl表達(dá)式結(jié)果為true時(shí)可以訪問
     * anonymous           |   匿名可以訪問
     * denyAll             |   用戶不能訪問
     * fullyAuthenticated  |   用戶完全認(rèn)證可以訪問(非remember-me下自動(dòng)登錄)
     * hasAnyAuthority     |   如果有參數(shù),參數(shù)表示權(quán)限,則其中任何一個(gè)權(quán)限可以訪問
     * hasAnyRole          |   如果有參數(shù),參數(shù)表示角色,則其中任何一個(gè)角色可以訪問
     * hasAuthority        |   如果有參數(shù),參數(shù)表示權(quán)限,則其權(quán)限可以訪問
     * hasIpAddress        |   如果有參數(shù),參數(shù)表示IP地址,如果用戶IP和參數(shù)匹配,則可以訪問
     * hasRole             |   如果有參數(shù),參數(shù)表示角色,則其角色可以訪問
     * permitAll           |   用戶可以任意訪問
     * rememberMe          |   允許通過(guò)remember-me登錄的用戶訪問
     * authenticated       |   用戶登錄后可訪問
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
                // CRSF禁用,因?yàn)椴皇褂胹ession
                .csrf().disable()
                // 認(rèn)證失敗處理類
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 過(guò)濾請(qǐng)求
                .authorizeRequests()
                // 對(duì)于登錄login 允許匿名訪問
                .antMatchers("/login").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/file/**",
                        "/**/*.js"
                ).permitAll()
                // 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();
        httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
    /**
     * 強(qiáng)散列哈希加密實(shí)現(xiàn)
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
    /**
     * 身份認(rèn)證接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }
}
  • @EnableGlobalMethodSecurity :當(dāng)我們想要開啟spring方法級(jí)安全時(shí),只需要在任何 @Configuration實(shí)例上使用 @EnableGlobalMethodSecurity 注解就能達(dá)到此目的。
  • prePostEnabled = true 會(huì)解鎖 @PreAuthorize 和 @PostAuthorize 兩個(gè)注解。從名字就可以看出@PreAuthorize 注解會(huì)在方法執(zhí)行前進(jìn)行驗(yàn)證,而 @PostAuthorize 注解會(huì)在方法執(zhí)行后進(jìn)行驗(yàn)證。
/**
 * 自定義權(quán)限實(shí)現(xiàn),ss取自SpringSecurity首字母
 */
@Service("ss")
public class PermissionService
{
    /** 所有權(quán)限標(biāo)識(shí) */
    private static final String ALL_PERMISSION = "*:*:*";
    /** 管理員角色權(quán)限標(biāo)識(shí) */
    private static final String SUPER_ADMIN = "admin";
    private static final String ROLE_DELIMETER = ",";
    private static final String PERMISSION_DELIMETER = ",";
    @Autowired
    private TokenService tokenService;
    /**
     * 驗(yàn)證用戶是否具備某權(quán)限
     *
     * @param permission 權(quán)限字符串
     * @return 用戶是否具備某權(quán)限
     */
    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);
    }
    /**
     * 驗(yàn)證用戶是否不具備某權(quán)限,與 hasPermi邏輯相反
     *
     * @param permission 權(quán)限字符串
     * @return 用戶是否不具備某權(quán)限
     */
    public boolean lacksPermi(String permission)
    {
        return hasPermi(permission) != true;
    }
    /**
     * 驗(yàn)證用戶是否具有以下任意一個(gè)權(quán)限
     *
     * @param permissions 以 PERMISSION_NAMES_DELIMETER 為分隔符的權(quán)限列表
     * @return 用戶是否具有以下任意一個(gè)權(quán)限
     */
    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;
    }
    /**
     * 判斷用戶是否擁有某個(gè)角色
     *
     * @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;
    }
    /**
     * 驗(yàn)證用戶是否不具備某角色,與 isRole邏輯相反。
     *
     * @param role 角色名稱
     * @return 用戶是否不具備某角色
     */
    public boolean lacksRole(String role)
    {
        return hasRole(role) != true;
    }
    /**
     * 驗(yàn)證用戶是否具有以下任意一個(gè)角色
     *
     * @param roles 以 ROLE_NAMES_DELIMETER 為分隔符的角色列表
     * @return 用戶是否具有以下任意一個(gè)角色
     */
    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;
    }
    /**
     * 判斷是否包含權(quán)限
     *
     * @param permissions 權(quán)限列表
     * @param permission 權(quán)限字符串
     * @return 用戶是否具備某權(quán)限
     */
    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():可傳入多個(gè)String參數(shù),用于匹配多個(gè)請(qǐng)求API。結(jié)合permitAll(),可同時(shí)對(duì)多個(gè)請(qǐng)求設(shè)置忽略認(rèn)證。
  • and():用于表示上一項(xiàng)配置已經(jīng)結(jié)束,下一項(xiàng)配置將開始。

創(chuàng)建一個(gè)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
    {
    	// 根據(jù)賬戶號(hào)查詢用戶信息
        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("對(duì)不起,您的賬號(hào):" + username + " 已被刪除");
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            log.info("登錄用戶:{} 已被停用.", username);
            throw new BaseException("對(duì)不起,您的賬號(hào):" + username + " 已停用");
        }
        return createLoginUser(user);
    }
    public UserDetails createLoginUser(SysUser user)
    {
    	// 獲取用戶權(quán)限
        return new LoginUser(user, permissionService.getMenuPermission(user));
    }
}

當(dāng)用戶請(qǐng)求時(shí),Security便會(huì)攔截請(qǐng)求,取出其中的username字段,從Service中查詢?cè)撡~戶,并將信息填充到一個(gè)userDetails對(duì)象中返回。這樣Security便拿到了填充后的userDetails對(duì)象,也就是獲取到了包括權(quán)限在內(nèi)的用戶信息。

過(guò)濾規(guī)則

WebSecurityConfigurerAdapter.configure(HttpSecurity http)

授權(quán)方式

Security的授權(quán)方式有兩種:

  • WEB授權(quán)(基于請(qǐng)求URL),在configure(HttpSecurity httpSecurity)中設(shè)置。
  • 方法授權(quán)(在方法上使用注解授權(quán))

WEB授權(quán)

@Override
httpSecurity.authorizeRequests() // 對(duì)請(qǐng)求進(jìn)行授權(quán)
    .antMatchers("/test1").hasAuthority("p1") // 設(shè)置/test1接口需要p1權(quán)限
    .antMatchers("/test2").hasAnyRole("admin", "manager") // 設(shè)置/test2接口需要admin或manager角色
    .and()
    .headers().frameOptions().disable();

其中:

  • hasAuthority(“p1”)表示需要p1權(quán)限
  • hasAnyAuthority(“p1”, “p2”)表示p1或p2權(quán)限皆可

同理:

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

方法授權(quán)

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

順序優(yōu)先級(jí)

校驗(yàn)是按照配置的順序從上往下進(jìn)行。若某個(gè)條件已匹配過(guò),則后續(xù)同條件的匹配將不再被執(zhí)行。也就是說(shuō),若多條規(guī)則是相互矛盾的,則只有第一條規(guī)則生效。典型地:

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

但若是包含關(guān)系,則需要將細(xì)粒度的規(guī)則放在前面:

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

登出

httpSecurity.logoutUrl()和httpSecurity.addLogoutHandler()是沖突的,通常只用其中之一。對(duì)于使用token且服務(wù)端不進(jìn)行存儲(chǔ)的情況,不需要請(qǐng)求服務(wù)端進(jìn)行登出,直接由前端將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()禁用跨域安全驗(yàn)證。

認(rèn)證失敗處理類

/**
 * 認(rèn)證失敗處理類 返回未授權(quán)
 */
@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("請(qǐng)求訪問:{},認(rèn)證失敗,無(wú)法訪問系統(tǒng)資源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

到此這篇關(guān)于Spring Security實(shí)現(xiàn)基于角色的訪問控制框架的文章就介紹到這了,更多相關(guān)Spring Security訪問控制框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java如何實(shí)現(xiàn)List自定義排序

    Java如何實(shí)現(xiàn)List自定義排序

    這篇文章主要介紹了Java如何實(shí)現(xiàn)List自定義排序,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-09-09
  • Java Resource路徑整理總結(jié)

    Java Resource路徑整理總結(jié)

    這篇文章主要介紹了 Java Resource路徑整理總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • java8 BigDecimal類型的List求和方式

    java8 BigDecimal類型的List求和方式

    這篇文章主要介紹了java8 BigDecimal類型的List求和方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-11-11
  • springboot如何將http轉(zhuǎn)https

    springboot如何將http轉(zhuǎn)https

    這篇文章主要介紹了springboot如何將http轉(zhuǎn)https,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • java 連接Redis的小例子

    java 連接Redis的小例子

    這篇文章介紹了java 連接Redis的小例子,有需要的朋友可以參考一下
    2013-09-09
  • Java解釋器的運(yùn)行過(guò)程介紹

    Java解釋器的運(yùn)行過(guò)程介紹

    今天小編就為大家分享一篇關(guān)于Java解釋器的運(yùn)行過(guò)程介紹,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-04-04
  • java基于OpenGL ES實(shí)現(xiàn)渲染實(shí)例

    java基于OpenGL ES實(shí)現(xiàn)渲染實(shí)例

    這篇文章主要介紹了java基于OpenGL ES實(shí)現(xiàn)渲染,實(shí)例分析了OpenGL渲染操作的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • Java使用BigDecimal公式精確計(jì)算及精度丟失問題

    Java使用BigDecimal公式精確計(jì)算及精度丟失問題

    在工作中經(jīng)常會(huì)遇到數(shù)值精度問題,比如說(shuō)使用float或者double的時(shí)候,可能會(huì)有精度丟失問題,下面這篇文章主要給大家介紹了關(guān)于Java使用BigDecimal公式精確計(jì)算及精度丟失問題的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • @RequestBody 部分屬性沒有轉(zhuǎn)化成功的處理

    @RequestBody 部分屬性沒有轉(zhuǎn)化成功的處理

    這篇文章主要介紹了@RequestBody 部分屬性沒有轉(zhuǎn)化成功的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • springboot打jar包之后下載文件的路徑問題

    springboot打jar包之后下載文件的路徑問題

    這篇文章主要介紹了springboot打jar包之后下載文件的路徑問題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論

金秀| 金湖县| 洛隆县| 馆陶县| 浠水县| 红安县| 娄烦县| 平度市| 泉州市| 小金县| 黔江区| 谢通门县| 会昌县| 于田县| 延安市| 邵武市| 慈利县| 桐柏县| 穆棱市| 大港区| 吴旗县| 恩平市| 吉首市| 嵊泗县| 上饶县| 高碑店市| 巴彦淖尔市| 肃南| 昭苏县| 司法| 微山县| 威海市| 文安县| 榆社县| 贡嘎县| 苏州市| 察隅县| 墨玉县| 青州市| 忻城县| 九龙坡区|