SpringSecurity概念及整合ssm框架的示例詳解
基本概念
Spring中提供安全認(rèn)證服務(wù)的框架,認(rèn)證:驗(yàn)證用戶密碼是否正確的過程,授權(quán):對(duì)用戶能訪問的資源進(jìn)行控制
用戶登錄系統(tǒng)時(shí)我們協(xié)助 SpringSecurity 把用戶對(duì)應(yīng)的角色、權(quán)限組裝好,同時(shí)把各個(gè)資源所要求的權(quán)限信息設(shè)定好,剩下的“登錄驗(yàn)證”、“權(quán)限驗(yàn)證”等等工作都交給SpringSecurity。
權(quán)限管理過程中的相關(guān)概念
主體
英文單詞:principal
使用系統(tǒng)的用戶或設(shè)備或從其他系統(tǒng)遠(yuǎn)程登錄的用戶等等。簡(jiǎn)單說就是誰使用系統(tǒng)誰就是主體。
認(rèn)證
英文單詞:authentication
權(quán)限管理系統(tǒng)確認(rèn)一個(gè)主體的身份,允許主體進(jìn)入系統(tǒng)。簡(jiǎn)單說就是“主體”證明自己是誰?;\統(tǒng)的認(rèn)為就是以前所做的登錄操作。
授權(quán)
英文單詞:authorization
將操作系統(tǒng)的“權(quán)力”“授予”“主體”,這樣主體就具備了操作系統(tǒng)中特定功能的能力。
所以簡(jiǎn)單來說,授權(quán)就是給用戶分配權(quán)限。
使用
pom文件中加入spring security的依賴
<!-- SpringSecurity 對(duì) Web 應(yīng)用進(jìn)行權(quán)限管理 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.2.10.RELEASE</version> </dependency> <!-- SpringSecurity 配置 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.2.10.RELEASE</version> </dependency> <!-- SpringSecurity 標(biāo)簽庫 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>4.2.10.RELEASE</version> </dependency>
web.xml加入SpringSecurity控制權(quán)限的Filte
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
加入配置類
package com.atguigu.crowd.mvc.config; import com.atguigu.crowd.constant.CrowdConstant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.access.AccessDeniedHandler; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Configuration// 表示當(dāng)前類是一個(gè)配置類 @EnableWebSecurity// 啟用web環(huán)境下權(quán)限控制 // prePostEnabled = true的作用:保證@PreAuthorize、@PreAuthorize、@PreFilter、@PostFilter生效 @EnableGlobalMethodSecurity(prePostEnabled = true)// 啟用全局方法權(quán)限控制 public class WebApplicationConfig extends WebSecurityConfigurerAdapter { @Autowired CrowdUserDetailsService crowdUserDetailsService; @Autowired BCryptPasswordEncoder passwordEncoder; @Override protected void configure(HttpSecurity security) throws Exception { security .authorizeRequests() .antMatchers("/admin/to/login/page.html") .permitAll()// 允許無條件訪問登錄請(qǐng)求 .antMatchers("/bootstrap/**", "/crowd/**", "/css/**", "/fonts/**", "/img/**", "/jquery/**", "/layer/**", "/script/**", "/ztree/**") .permitAll()// 允許無條件訪問靜態(tài)文件 .antMatchers("/admin/get/page.html") .hasAuthority("user:get") // 擁有user:get權(quán)限才可以訪問用戶維護(hù) .antMatchers("/role/to/role/page.html") .hasRole("經(jīng)理")// 擁有經(jīng)理角色才可以訪問角色維護(hù) .antMatchers("/admin/get/page.html") .access("(hasAnyRole('經(jīng)理')) and hasAuthority('user:get')") .anyRequest() .authenticated()// 其他任何資源都要登錄才可以訪問 .and() .exceptionHandling() .accessDeniedHandler(new AccessDeniedHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { request.setAttribute("exception", new Exception(CrowdConstant.MESSAGE_ACCESS_DENIED)); request.getRequestDispatcher("/WEB-INF/error/system_error.jsp").forward(request, response); } })// 出現(xiàn)異常的處理方法 .and() .csrf() .disable()// 禁用跨站請(qǐng)求偽造功能 .formLogin()// 開啟表單登錄功能 .loginPage("/admin/to/login/page.html")// 設(shè)置默認(rèn)的登錄頁面 .loginProcessingUrl("/security/do/login.html")// 處理登錄請(qǐng)求的地址 .defaultSuccessUrl("/admin/to/admin-main/page.html")// 登錄成功后要前往的地址 .usernameParameter("loginAcct")// 賬號(hào)的請(qǐng)求參數(shù)名 .passwordParameter("userPswd")// 密碼的請(qǐng)求參數(shù)名 .and() .logout() .logoutUrl("/security/do/logout.html")// 退出登錄訪問的地址 .logoutSuccessUrl("/admin/to/login/page.html")// 退出頁面成功要去的地址 ; } @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception { // builder // .inMemoryAuthentication()// 開啟內(nèi)存認(rèn)證 // .withUser("tom")// 設(shè)置賬號(hào) // .password("123123")// 設(shè)置密碼 // .roles("ADMIN")// 設(shè)置角色 // ; // 使用基于數(shù)據(jù)庫的認(rèn)證 builder.userDetailsService(crowdUserDetailsService).passwordEncoder(passwordEncoder); } }
注解@PreAuthorize規(guī)定角色權(quán)限
需要在SpringSecurity中標(biāo)注
@EnableGlobalMethodSecurity(prePostEnabled = true)
/** * 根據(jù)關(guān)鍵字查詢分頁信息 * * @param pageNum 當(dāng)前頁碼 * @param pageSize 每頁顯示條數(shù) * @param keyword 關(guān)鍵字 * @return 分頁信息 */ @PreAuthorize("hasAuthority('role:get') or hasAnyRole('經(jīng)理')")// 需要有role:get權(quán)限才可以訪問 @ResponseBody @GetMapping("/role/get/page/info.json") public ResultEntity<PageInfo<Role>> getPageInfo( @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "4") Integer pageSize, @RequestParam(value = "keyword", defaultValue = "") String keyword ) { // 獲取頁面數(shù)據(jù) PageInfo<Role> pageInfo = roleService.getRoleByKeyword(pageNum, pageSize, keyword); // 如果出現(xiàn)異常,dispatcherServlet會(huì)進(jìn)行處理 return ResultEntity.successWithData(pageInfo); }
@PostAuthorize
先執(zhí)行方法然后根據(jù)方法返回值判斷是否具備權(quán)限。
例如:查詢一個(gè) Admin 對(duì)象,在@PostAuthorize 注解中和當(dāng)前登錄的 Admin 對(duì)象進(jìn)行比較,如果不一致,則判斷為不能訪問。實(shí)現(xiàn)“只能查自己”效果。
@PostAuthorize("returnObject.data.loginAcct == principal.username")
使用 returnObject 獲取到方法返回值,使用 principal 獲取到當(dāng)前登錄用戶的主體對(duì)象。
通過故意寫錯(cuò)表達(dá)式,然后從異常信息中發(fā)現(xiàn)表達(dá)式訪問的是下面這個(gè)類的屬性:
org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
@PreFilter
在方法執(zhí)行前對(duì)傳入的參數(shù)進(jìn)行過濾。只能對(duì)集合類型的數(shù)據(jù)進(jìn)行過濾。
@PreFilter(value="filterObject%2==0") @ResponseBody @RequestMapping("/admin/test/pre/filter") public ResultEntity<List<Integer>> saveList(@RequestBody List<Integer> valueList) { return ResultEntity.successWithData(valueList); }
@PostFilter
在方法執(zhí)行后對(duì)方法返回值進(jìn)行過濾。只能對(duì)集合類型的數(shù)據(jù)進(jìn)行過濾。
CrowdUserDetailsService
其實(shí)返回的就是SpringSecurity的User對(duì)象,只是做了一層封裝。
package com.atguigu.crowd.mvc.config; import com.atguigu.crowd.entity.Admin; import com.atguigu.crowd.entity.Role; import com.atguigu.crowd.service.api.AdminService; import com.atguigu.crowd.service.api.AuthService; import com.atguigu.crowd.service.api.RoleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; @Component public class CrowdUserDetailsService implements UserDetailsService { @Autowired private AdminService adminService; @Autowired private RoleService roleService; @Autowired private AuthService authService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1、根據(jù)賬號(hào)名稱查找Admin對(duì)象 Admin admin = adminService.getAdminByLoginAcct(username); // 2、獲取adminId Integer adminId = admin.getId(); // 3、根據(jù)adminId查詢角色信息 List<Role> roles = roleService.getAssignedRole(adminId); // 4、根據(jù)adminId查詢權(quán)限名字信息 List<String> auths = authService.getAssignedAuthNameByAdminId(adminId); // 5、創(chuàng)建集合對(duì)象來存儲(chǔ)GrantedAuthority ArrayList<GrantedAuthority> authorities = new ArrayList<>(); // 6、遍歷存入角色消息 for (int i = 0; i < roles.size(); i++) { // 需要加前綴,因?yàn)镾pringSecurity就是通過ROLE_來區(qū)分是角色還是權(quán)限 String roleName = "ROLE_" + roles.get(i).getName(); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roleName); authorities.add(authority); } // 7、遍歷存入權(quán)限信息 for (int i = 0; i < auths.size(); i++) { String authName = auths.get(i); SimpleGrantedAuthority authority = new SimpleGrantedAuthority(authName); authorities.add(authority); } // 8、封裝到SecurityAdmin對(duì)象中,它繼承了SpringSecurity的User類 SecurityAdmin securityAdmin = new SecurityAdmin(admin, authorities); return securityAdmin; } }
BCryptPasswordEncoder
它是SpringSecurity自帶的密碼加密與驗(yàn)證的類。
頁面元素控制
取出當(dāng)前登錄對(duì)象
<%--需要導(dǎo)入taglib--%> <%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %> <%--principal 是我們自己封裝的 SecurityAdmin 對(duì)象(返回的SpringSecurity的User對(duì)象)--%> <security:authentication property="principal.originalAdmin.userName"/>
標(biāo)簽庫控制
<%--需要導(dǎo)入taglib--%> <%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %> <security:authorize access="hasRole('經(jīng)理')"> <!-- 開始和結(jié)束標(biāo)簽之間是要進(jìn)行權(quán)限控制的部分。檢測(cè)當(dāng)前用戶是否有權(quán)限,有權(quán)限 就顯示這里的內(nèi)容,沒有權(quán)限就不顯示。 --> …… </security:authorize>
access屬性可以傳入權(quán)限控制相關(guān)的表達(dá)式。
### 標(biāo)簽庫控制 ```html <%--需要導(dǎo)入taglib--%> <%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %> <security:authorize access="hasRole('經(jīng)理')"> <!-- 開始和結(jié)束標(biāo)簽之間是要進(jìn)行權(quán)限控制的部分。檢測(cè)當(dāng)前用戶是否有權(quán)限,有權(quán)限 就顯示這里的內(nèi)容,沒有權(quán)限就不顯示。 --> …… </security:authorize>
access屬性可以傳入權(quán)限控制相關(guān)的表達(dá)式。
到此這篇關(guān)于SpringSecurity概念以及整合ssm框架的文章就介紹到這了,更多相關(guān)SpringSecurity整合ssm框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
我總結(jié)的幾種@Transactional失效原因說明
這篇文章主要是我總結(jié)的幾種@Transactional失效原因說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11springboot啟動(dòng)加載CommandLineRunner @PostConstruct問題
這篇文章主要介紹了springboot啟動(dòng)加載CommandLineRunner @PostConstruct問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08idea 右鍵項(xiàng)目沒有run 運(yùn)行選項(xiàng)
這篇文章主要介紹了idea 右鍵項(xiàng)目沒有run 運(yùn)行選項(xiàng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Java中RSA加密解密的實(shí)現(xiàn)方法分析
這篇文章主要介紹了Java中RSA加密解密的實(shí)現(xiàn)方法,結(jié)合具體實(shí)例形式分析了java實(shí)現(xiàn)RSA加密解密算法的具體步驟與相關(guān)操作技巧,并附帶了關(guān)于RSA算法密鑰長(zhǎng)度/密文長(zhǎng)度/明文長(zhǎng)度的參考說明,需要的朋友可以參考下2017-07-07Spring IOC簡(jiǎn)單理解及創(chuàng)建對(duì)象的方式
這篇文章主要介紹了Spring IOC簡(jiǎn)單理解及創(chuàng)建對(duì)象的方式,本文通過兩種方式給大家介紹創(chuàng)建對(duì)象的方法,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09JAVA中JSONObject對(duì)象和Map對(duì)象之間的相互轉(zhuǎn)換
這篇文章主要介紹了JAVA中JSONObject對(duì)象和Map對(duì)象之間的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01MybatisPlus 不修改全局策略和字段注解如何將字段更新為null
這篇文章主要介紹了MybatisPlus 不修改全局策略和字段注解如何將字段更新為null,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器
這篇文章主要介紹了java Socket實(shí)現(xiàn)簡(jiǎn)單模擬HTTP服務(wù)器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05