javascript
看完这一篇,你就对 Spring Security 略窥门径了 | 原力计划
作者 |?BoCong-Deng
來源 |?CSDN 博客,責(zé)編 | 夕顏
頭圖 |?CSDN 下載自東方 IC
出品 | CSDN(ID:CSDNnews)
寫在前面
開發(fā)Web應(yīng)用,對(duì)頁(yè)面的安全控制通常是必須的。比如:對(duì)于沒有訪問權(quán)限的用戶需要轉(zhuǎn)到登錄表單頁(yè)面。要實(shí)現(xiàn)訪問控制的方法多種多樣,可以通過Aop、攔截器實(shí)現(xiàn),也可以通過框架實(shí)現(xiàn),例如:Apache Shiro、Spring Security。我們這里要講的Spring Security 就是一個(gè)Spring生態(tài)中關(guān)于安全方面的框架。它能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案。
默認(rèn)認(rèn)證用戶名密碼
項(xiàng)目pom.xml添加spring-boot-starter-security依賴
1<dependency> 2????<groupId>org.springframework.boot</groupId> 3????<artifactId>spring-boot-starter-security</artifactId> 4</dependency>重啟你的應(yīng)用。再次打開頁(yè)面,你講看到一個(gè)登錄頁(yè)面
既然跳到了登錄頁(yè)面,那么這個(gè)時(shí)候我們就會(huì)想,這個(gè)登錄的用戶名以及密碼是什么呢?讓我們來從SpringBoot源碼尋找一下。你搜一下輸出日志,會(huì)看到下面一段輸出:
這段日志是UserDetailsServiceAutoConfiguration類里面的如下方法輸出的:
通過上面的這個(gè)類,我們可以看出,是SecurityProperties這個(gè)Bean管理了用戶名和密碼。在SecurityProperties里面的一個(gè)內(nèi)部靜態(tài)類User類里面,管理了默認(rèn)的認(rèn)證的用戶名與密碼。代碼如下
1@ConfigurationProperties(2????prefix?=?"spring.security"3)4public?class?SecurityProperties?{5????public?static?final?int?BASIC_AUTH_ORDER?=?2147483642;6????public?static?final?int?IGNORED_ORDER?=?-2147483648;7????public?static?final?int?DEFAULT_FILTER_ORDER?=?-100;8????private?final?SecurityProperties.Filter?filter?=?new?SecurityProperties.Filter();9????private?SecurityProperties.User?user?=?new?SecurityProperties.User(); 10 11????public?SecurityProperties()?{ 12????} 13 14????public?SecurityProperties.User?getUser()?{ 15????????return?this.user; 16????} 17 18????public?SecurityProperties.Filter?getFilter()?{ 19????????return?this.filter; 20????} 21 22????public?static?class?User?{ 23????????private?String?name?=?"user"; 24????????private?String?password?=?UUID.randomUUID().toString(); 25????????private?List<String>?roles?=?new?ArrayList(); 26????????private?boolean?passwordGenerated?=?true; 27 28????????public?User()?{ 29????????} 30 31????????public?String?getName()?{ 32????????????return?this.name; 33????????} 34 35????????public?void?setName(String?name)?{ 36????????????this.name?=?name; 37????????} 38 39????????public?String?getPassword()?{ 40????????????return?this.password; 41????????} 42 43????????public?void?setPassword(String?password)?{ 44????????????if?(StringUtils.hasLength(password))?{ 45????????????????this.passwordGenerated?=?false; 46????????????????this.password?=?password; 47????????????} 48????????} 49 50????????public?List<String>?getRoles()?{ 51????????????return?this.roles; 52????????} 53 54????????public?void?setRoles(List<String>?roles)?{ 55????????????this.roles?=?new?ArrayList(roles); 56????????} 57 58????????public?boolean?isPasswordGenerated()?{ 59????????????return?this.passwordGenerated; 60????????} 61????} 62 63????public?static?class?Filter?{ 64????????private?int?order?=?-100; 65????????private?Set<DispatcherType>?dispatcherTypes; 66 67????????public?Filter()?{ 68????????????this.dispatcherTypes?=?new?HashSet(Arrays.asList(DispatcherType.ASYNC,?DispatcherType.ERROR,?DispatcherType.REQUEST)); 69????????} 70 71????????public?int?getOrder()?{ 72????????????return?this.order; 73????????} 74 75????????public?void?setOrder(int?order)?{ 76????????????this.order?=?order; 77????????} 78 79????????public?Set<DispatcherType>?getDispatcherTypes()?{ 80????????????return?this.dispatcherTypes; 81????????} 82 83????????public?void?setDispatcherTypes(Set<DispatcherType>?dispatcherTypes)?{ 84????????????this.dispatcherTypes?=?dispatcherTypes; 85????????} 86????} 87}綜上所述,security默認(rèn)的用戶名是user, 默認(rèn)密碼是應(yīng)用啟動(dòng)的時(shí)候,通過UUID算法隨機(jī)生成的,默認(rèn)的role是"USER"。當(dāng)然,如果我們想簡(jiǎn)單改一下這個(gè)用戶名密碼,可以在application.properties配置你的用戶名密碼,例如
當(dāng)然這只是一個(gè)初級(jí)的配置,更復(fù)雜的配置,可以分不用角色,在控制范圍上,能夠攔截到方法級(jí)別的權(quán)限控制。
內(nèi)存用戶名密碼認(rèn)證
在上面的內(nèi)容,我們什么都沒做,就添加了spring-boot-starter-security依賴,整個(gè)應(yīng)用就有了默認(rèn)的認(rèn)證安全機(jī)制。下面,我們來定制用戶名密碼。寫一個(gè)繼承了 WebSecurityConfigurerAdapter的配置類,具體內(nèi)容如下
1import?org.springframework.context.annotation.Configuration;2import?org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;3import?org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;4import?org.springframework.security.config.annotation.web.builders.HttpSecurity;5import?org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;6import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;78@Configuration9@EnableWebSecurity 10@EnableGlobalMethodSecurity(prePostEnabled?=?true,?securedEnabled?=?true,?jsr250Enabled?=?true) 11public?class?WebSecurityConfig?extends?WebSecurityConfigurerAdapter?{ 12????@Override 13????protected?void?configure(HttpSecurity?http)?throws?Exception?{ 14????????super.configure(http); 15????} 16 17????@Override 18????protected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{ 19????????auth.inMemoryAuthentication() 20????????????????.passwordEncoder(new?BCryptPasswordEncoder()) 21????????????????.withUser("admin") 22????????????????.password(new?BCryptPasswordEncoder().encode("1234567")) 23????????????????.roles("USER"); 24????} 25}這里對(duì)上面的代碼進(jìn)行簡(jiǎn)要說明:
Spring security 5.0中新增了多種加密方式,也改變了默認(rèn)的密碼格式。需要修改一下configure中的代碼,我們要將前端傳過來的密碼進(jìn)行某種方式加密,Spring Security 官方推薦的是使用bcrypt加密方式。inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()),這相當(dāng)于登陸時(shí)用BCrypt加密方式對(duì)用戶密碼進(jìn)行處理。以前的".password("123")" 變成了 “.password(new BCryptPasswordEncoder().encode("123"))”,這相當(dāng)于對(duì)內(nèi)存中的密碼進(jìn)行Bcrypt編碼加密。如果比對(duì)時(shí)一致,說明密碼正確,才允許登陸。
通過 @EnableWebSecurity注解開啟Spring Security的功能。使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個(gè)注解,可以開啟security的注解,我們可以在需要控制權(quán)限的方法上面使用@PreAuthorize,@PreFilter這些注解。
繼承 WebSecurityConfigurerAdapter 類,并重寫它的方法來設(shè)置一些web安全的細(xì)節(jié)。我們結(jié)合@EnableWebSecurity注解和繼承WebSecurityConfigurerAdapter,來給我們的系統(tǒng)加上基于web的安全機(jī)制。
在configure(HttpSecurity http)方法里面,我們進(jìn)入到源碼中,就會(huì)看到默認(rèn)的認(rèn)證代碼是:
從方法名我們基本可以看懂這些方法的功能。上面的那個(gè)默認(rèn)的登錄頁(yè)面,就是SpringBoot默認(rèn)的用戶名密碼認(rèn)證的login頁(yè)面。我們使用SpringBoot默認(rèn)的配置super.configure(http),它通過 authorizeRequests() 定義哪些URL需要被保護(hù)、哪些不需要被保護(hù)。默認(rèn)配置是所有訪問頁(yè)面都需要認(rèn)證,才可以訪問。
通過 formLogin() 定義當(dāng)需要用戶登錄時(shí)候,轉(zhuǎn)到的登錄頁(yè)面。
configureGlobal(AuthenticationManagerBuilder auth) 方法,在內(nèi)存中創(chuàng)建了一個(gè)用戶,該用戶的名稱為root,密碼為root,用戶角色為USER。這個(gè)默認(rèn)的登錄頁(yè)面是怎么冒出來的呢?是的,SpringBoot內(nèi)置的,SpringBoot甚至給我們做好了一個(gè)極簡(jiǎn)的登錄頁(yè)面。這個(gè)登錄頁(yè)面是通過Filter實(shí)現(xiàn)的。具體的實(shí)現(xiàn)類是org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter。同時(shí),這個(gè)DefaultLoginPageGeneratingFilter也是SpringBoot的默認(rèn)內(nèi)置的Filter。
輸入用戶名,密碼,點(diǎn)擊Login。不過,我們發(fā)現(xiàn),SpringBoot應(yīng)用的啟動(dòng)日志還是打印了如下一段:
但實(shí)際上,已經(jīng)使用了我們定制的用戶名密碼了。如果我們要配置多個(gè)用戶,多個(gè)角色,可參考使用如下示例的代碼:
1@Override2????protected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{3????????auth.inMemoryAuthentication()4????????????????.passwordEncoder(new?BCryptPasswordEncoder())5????????????????.withUser("admin")6????????????????.password(new?BCryptPasswordEncoder().encode("1234567"))7????????????????.roles("USER")8????????????????.and()9????????????????.withUser("admin1") 10????????????????.password(new?BCryptPasswordEncoder().encode("123")) 11????????????????.roles("ADMIN",?"USER"); 12????}角色權(quán)限控制
當(dāng)我們的系統(tǒng)功能模塊當(dāng)需求發(fā)展到一定程度時(shí),會(huì)不同的用戶,不同角色使用我們的系統(tǒng)。這樣就要求我們的系統(tǒng)可以做到,能夠?qū)Σ煌南到y(tǒng)功能模塊,開放給對(duì)應(yīng)的擁有其訪問權(quán)限的用戶使用。Spring Security提供了Spring EL表達(dá)式,允許我們?cè)诙xURL路徑訪問(@RequestMapping)的方法上面添加注解,來控制訪問權(quán)限。在標(biāo)注訪問權(quán)限時(shí),根據(jù)對(duì)應(yīng)的表達(dá)式返回結(jié)果,控制訪問權(quán)限:
1true,表示有權(quán)限 2fasle,表示無(wú)權(quán)限Spring Security可用表達(dá)式對(duì)象的基類是SecurityExpressionRoot。
1public?abstract?class?SecurityExpressionRoot?implements?SecurityExpressionOperations?{2????protected?final?Authentication?authentication;3????private?AuthenticationTrustResolver?trustResolver;4????private?RoleHierarchy?roleHierarchy;5????private?Set<String>?roles;6????private?String?defaultRolePrefix?=?"ROLE_";7????public?final?boolean?permitAll?=?true;8????public?final?boolean?denyAll?=?false;9????private?PermissionEvaluator?permissionEvaluator;10????public?final?String?read?=?"read";11????public?final?String?write?=?"write";12????public?final?String?create?=?"create";13????public?final?String?delete?=?"delete";14????public?final?String?admin?=?"administration";1516????public?SecurityExpressionRoot(Authentication?authentication)?{17????????if?(authentication?==?null)?{18????????????throw?new?IllegalArgumentException("Authentication?object?cannot?be?null");19????????}?else?{20????????????this.authentication?=?authentication;21????????}22????}2324????public?final?boolean?hasAuthority(String?authority)?{25????????return?this.hasAnyAuthority(authority);26????}2728????public?final?boolean?hasAnyAuthority(String...?authorities)?{29????????return?this.hasAnyAuthorityName((String)null,?authorities);30????}3132????public?final?boolean?hasRole(String?role)?{33????????return?this.hasAnyRole(role);34????}3536????public?final?boolean?hasAnyRole(String...?roles)?{37????????return?this.hasAnyAuthorityName(this.defaultRolePrefix,?roles);38????}3940????private?boolean?hasAnyAuthorityName(String?prefix,?String...?roles)?{41????????Set<String>?roleSet?=?this.getAuthoritySet();42????????String[]?var4?=?roles;43????????int?var5?=?roles.length;4445????????for(int?var6?=?0;?var6?<?var5;?++var6)?{46????????????String?role?=?var4[var6];47????????????String?defaultedRole?=?getRoleWithDefaultPrefix(prefix,?role);48????????????if?(roleSet.contains(defaultedRole))?{49????????????????return?true;50????????????}51????????}5253????????return?false;54????}5556????public?final?Authentication?getAuthentication()?{57????????return?this.authentication;58????}5960????public?final?boolean?permitAll()?{61????????return?true;62????}6364????public?final?boolean?denyAll()?{65????????return?false;66????}6768????public?final?boolean?isAnonymous()?{69????????return?this.trustResolver.isAnonymous(this.authentication);70????}7172????public?final?boolean?isAuthenticated()?{73????????return?!this.isAnonymous();74????}7576????public?final?boolean?isRememberMe()?{77????????return?this.trustResolver.isRememberMe(this.authentication);78????}7980????public?final?boolean?isFullyAuthenticated()?{81????????return?!this.trustResolver.isAnonymous(this.authentication)?&&?!this.trustResolver.isRememberMe(this.authentication);82????}8384????public?Object?getPrincipal()?{85????????return?this.authentication.getPrincipal();86????}8788????public?void?setTrustResolver(AuthenticationTrustResolver?trustResolver)?{89????????this.trustResolver?=?trustResolver;90????}9192????public?void?setRoleHierarchy(RoleHierarchy?roleHierarchy)?{93????????this.roleHierarchy?=?roleHierarchy;94????}9596????public?void?setDefaultRolePrefix(String?defaultRolePrefix)?{97????????this.defaultRolePrefix?=?defaultRolePrefix;98????}99 100????private?Set<String>?getAuthoritySet()?{ 101????????if?(this.roles?==?null)?{ 102????????????Collection<??extends?GrantedAuthority>?userAuthorities?=?this.authentication.getAuthorities(); 103????????????if?(this.roleHierarchy?!=?null)?{ 104????????????????userAuthorities?=?this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities); 105????????????} 106 107????????????this.roles?=?AuthorityUtils.authorityListToSet(userAuthorities); 108????????} 109 110????????return?this.roles; 111????} 112 113????public?boolean?hasPermission(Object?target,?Object?permission)?{ 114????????return?this.permissionEvaluator.hasPermission(this.authentication,?target,?permission); 115????} 116 117????public?boolean?hasPermission(Object?targetId,?String?targetType,?Object?permission)?{ 118????????return?this.permissionEvaluator.hasPermission(this.authentication,?(Serializable)targetId,?targetType,?permission); 119????} 120 121????public?void?setPermissionEvaluator(PermissionEvaluator?permissionEvaluator)?{ 122????????this.permissionEvaluator?=?permissionEvaluator; 123????} 124 125????private?static?String?getRoleWithDefaultPrefix(String?defaultRolePrefix,?String?role)?{ 126????????if?(role?==?null)?{ 127????????????return?role; 128????????}?else?if?(defaultRolePrefix?!=?null?&&?defaultRolePrefix.length()?!=?0)?{ 129????????????return?role.startsWith(defaultRolePrefix)???role?:?defaultRolePrefix?+?role; 130????????}?else?{ 131????????????return?role; 132????????} 133????} 134}通過閱讀源碼,我們可以更加深刻的理解其EL寫法,并在寫代碼的時(shí)候正確的使用。變量defaultRolePrefix硬編碼約定了role的前綴是"ROLE_"。同時(shí),我們可以看出hasRole跟hasAnyRole是一樣的。hasAnyRole是調(diào)用的hasAnyAuthorityName(defaultRolePrefix, roles)。所以,我們?cè)趯W(xué)習(xí)一個(gè)框架或者一門技術(shù)的時(shí)候,最準(zhǔn)確的就是源碼。通過源碼,我們可以更好更深入的理解技術(shù)的本質(zhì)。
SecurityExpressionRoot為我們提供的使用Spring EL表達(dá)式總結(jié)如下:
在Controller方法上添加@PreAuthorize這個(gè)注解,value="hasRole('ADMIN')")是Spring-EL expression,當(dāng)表達(dá)式值為true,標(biāo)識(shí)這個(gè)方法可以被調(diào)用。如果表達(dá)式值是false,標(biāo)識(shí)此方法無(wú)權(quán)限訪問。
在Spring Security里獲取當(dāng)前登錄認(rèn)證通過的用戶信息
如果我們想要在前端頁(yè)面顯示當(dāng)前登錄的用戶怎么辦呢?在在Spring Security里面怎樣獲取當(dāng)前登錄認(rèn)證通過的用戶信息?下面我們就來探討這個(gè)問題。其實(shí)很好辦。我們添加一個(gè)LoginFilter,默認(rèn)攔截所有請(qǐng)求,把當(dāng)前登錄的用戶放到系統(tǒng)session中即可。在Spring Security中,用戶信息保存在SecurityContextHolder中。Spring Security使用一個(gè)Authentication對(duì)象來持有所有系統(tǒng)的安全認(rèn)證相關(guān)的信息。這個(gè)信息的內(nèi)容格式如下:
1{2????"accountNonExpired":true,3????"accountNonLocked":true,4????"authorities":[{5????????"authority":"ROLE_ADMIN"6????},{7????????"authority":"ROLE_USER"8????}],9????"credentialsNonExpired":true, 10????"enabled":true, 11????"username":"root" 12}這個(gè)Authentication對(duì)象信息其實(shí)就是User實(shí)體的信息,類似如下(當(dāng)然,密碼沒放進(jìn)來)。
1public?class?User?implements?UserDetails,?CredentialsContainer?{2????private?String?password;3????private?final?String?username;4????private?final?Set<GrantedAuthority>?authorities;5????private?final?boolean?accountNonExpired;6????private?final?boolean?accountNonLocked;7????private?final?boolean?credentialsNonExpired;8????private?final?boolean?enabled;9????????.... 10}我們可以使用下面的代碼(Java)獲得當(dāng)前身份驗(yàn)證的用戶的名稱:
1Object?principal?=?SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 2 3if?(principal?instanceof?UserDetails)?{ 4????String?username?=?((UserDetails)principal).getUsername(); 5}?else?{ 6????String?username?=?principal.toString(); 7}通過調(diào)用getContext()返回的對(duì)象是SecurityContext的實(shí)例對(duì)象,該實(shí)例對(duì)象保存在ThreadLocal線程本地存儲(chǔ)中。使用Spring Security框架,通常的認(rèn)證機(jī)制都是返回UserDetails實(shí)例,通過如上這種方式,我們就可以拿到認(rèn)證登錄的用戶信息。
用數(shù)據(jù)庫(kù)存儲(chǔ)用戶和角色,實(shí)現(xiàn)安全認(rèn)證
很多時(shí)候,我們需要的是實(shí)現(xiàn)一個(gè)用數(shù)據(jù)庫(kù)存儲(chǔ)用戶和角色,實(shí)現(xiàn)系統(tǒng)的安全認(rèn)證。為了簡(jiǎn)化講解,本例中在權(quán)限角色上,我們簡(jiǎn)單設(shè)計(jì)兩個(gè)用戶角色:USER,ADMIN。我們?cè)O(shè)計(jì)頁(yè)面的權(quán)限如下:
首頁(yè)/ : 所有人可訪問
登錄頁(yè) /login: 所有人可訪問
普通用戶權(quán)限頁(yè) /httpapi, /httpsuite: 登錄后的用戶都可訪問
管理員權(quán)限頁(yè) /httpreport :僅管理員可訪問
無(wú)權(quán)限提醒頁(yè):當(dāng)一個(gè)用戶訪問了其沒有權(quán)限的頁(yè)面,我們使用全局統(tǒng)一的異常處理頁(yè)面提示。
配置Spring Security
我們首先使用Spring Security幫我們做登錄、登出的處理,以及當(dāng)用戶未登錄時(shí)只能訪問: http://localhost:8080/ 以及 http://localhost:8080/login 兩個(gè)頁(yè)面。同樣的,我們要寫一個(gè)繼承WebSecurityConfigurerAdapter的配置類:
1import?com.springboot.in.action.service.LightSwordUserDetailService;2import?org.springframework.context.annotation.Bean;3import?org.springframework.context.annotation.Configuration;4import?org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;5import?org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;6import?org.springframework.security.config.annotation.web.builders.HttpSecurity;7import?org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;8import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;9import?org.springframework.security.core.userdetails.UserDetailsService; 10 11/** 12?*?Created?by?jack?on?2017/4/27. 13?*/ 14 15@Configuration 16@EnableWebSecurity 17@EnableGlobalMethodSecurity(prePostEnabled?=?true,?securedEnabled?=?true,?jsr250Enabled?=?true) 18//使用@EnableGlobalMethodSecurity(prePostEnabled?=?true) 19//?這個(gè)注解,可以開啟security的注解,我們可以在需要控制權(quán)限的方法上面使用@PreAuthorize,@PreFilter這些注解。 20public?class?WebSecurityConfig?extends?WebSecurityConfigurerAdapter?{ 21????@Override 22????@Bean 23????public?UserDetailsService?userDetailsService()?{?//覆蓋寫userDetailsService方法?(1) 24????????return?new?AdminUserDetailService(); 25 26????} 27 28????/** 29?????*?If?subclassed?this?will?potentially?override?subclass?configure(HttpSecurity) 30?????* 31?????*?@param?http 32?????*?@throws?Exception 33?????*/ 34????@Override 35????protected?void?configure(HttpSecurity?http)?throws?Exception?{ 36????????//super.configure(http); 37????????http.csrf().disable(); 38 39????????http.authorizeRequests() 40????????????.antMatchers("/").permitAll() 41????????????.antMatchers("/amchart/**", 42????????????????"/bootstrap/**", 43????????????????"/build/**", 44????????????????"/css/**", 45????????????????"/dist/**", 46????????????????"/documentation/**", 47????????????????"/fonts/**", 48????????????????"/js/**", 49????????????????"/pages/**", 50????????????????"/plugins/**" 51????????????).permitAll()?//默認(rèn)不攔截靜態(tài)資源的url?pattern?(2) 52????????????.anyRequest().authenticated().and() 53????????????.formLogin().loginPage("/login")//?登錄url請(qǐng)求路徑?(3) 54????????????.defaultSuccessUrl("/httpapi").permitAll().and()?//?登錄成功跳轉(zhuǎn)路徑url(4) 55????????????.logout().permitAll(); 56 57????????http.logout().logoutSuccessUrl("/");?//?退出默認(rèn)跳轉(zhuǎn)頁(yè)面?(5) 58 59????} 60 61????@Override 62????protected?void?configure(AuthenticationManagerBuilder?auth)?throws?Exception?{ 63????????//AuthenticationManager使用我們的?Service來獲取用戶信息,Service可以自己寫,其實(shí)就是簡(jiǎn)單的讀取數(shù)據(jù)庫(kù)的操作 64????????auth.userDetailsService(());?//?(6) 65????} 66 67}上面的代碼只做了基本的配置,其中:
覆蓋寫userDetailsService方法,具體的AdminUserDetailsService實(shí)現(xiàn)類,就是之前說的獲取用戶信息的service層類。
默認(rèn)不攔截靜態(tài)資源的url pattern。我們也可以用下面的WebSecurity這個(gè)方式跳過靜態(tài)資源的認(rèn)證。
跳轉(zhuǎn)登錄頁(yè)面url請(qǐng)求路徑為/login,我們需要定義一個(gè)Controller把路徑映射到login.html。
登錄成功后跳轉(zhuǎn)的路徑為/httpapi
退出后跳轉(zhuǎn)到的url為/
認(rèn)證鑒權(quán)信息的Bean,采用我們自定義的從數(shù)據(jù)庫(kù)中獲取用戶信息的AdminUserDetailService類。
我們同樣使用@EnableGlobalMethodSecurity(prePostEnabled = true)這個(gè)注解,開啟security的注解,這樣我們可以在需要控制權(quán)限的方法上面使用@PreAuthorize,@PreFilter這些注解。
用戶退出
我們?cè)赾onfigure(HttpSecurity http)方法里面定義了任何權(quán)限都允許退出,當(dāng)然SpringBoot集成Security的默認(rèn)退出請(qǐng)求是/logout
1http.logout().logoutSuccessUrl("/");?//?退出默認(rèn)跳轉(zhuǎn)頁(yè)面?(4)配置錯(cuò)誤處理頁(yè)面
訪問發(fā)生錯(cuò)誤時(shí),跳轉(zhuǎn)到系統(tǒng)統(tǒng)一異常處理頁(yè)面。我們首先添加一個(gè)GlobalExceptionHandlerAdvice,使用@ControllerAdvice注解:
1import?org.springframework.web.bind.annotation.{ControllerAdvice,?ExceptionHandler}2import?org.springframework.web.context.request.WebRequest3import?org.springframework.web.servlet.ModelAndView45/**6??*?Created?by?jack?on?2017/4/27.7??*/8@ControllerAdvice9class?GlobalExceptionHandlerAdvice?{ 10??@ExceptionHandler(value?=?Exception.class)//表示捕捉到所有的異常,你也可以捕捉一個(gè)你自定義的異常 11????public?ModelAndView?exception(Exception?exception,?WebRequest?request){ 12????????ModelAndView?modelAndView?=?new?ModelAndView("/error"); 13????????modelAndView.addObject("errorMessage",?exception.getMessage()); 14????????modelAndView.addObject("stackTrace",?exception.getStackTrace()); 15????????return?modelAndView; 16????} 17}其中,@ExceptionHandler(value = Exception.class),表示捕捉到所有的異常,這里你也可以捕捉一個(gè)你自定義的異常。比如說,針對(duì)安全認(rèn)證的Exception,我們可以單獨(dú)定義處理。此處不再贅述。
原文鏈接:
https://blog.csdn.net/DBC_121/article/details/104740273
為了讓大家更好地了解開發(fā)者,CSDN特發(fā)起“開發(fā)者與AI大調(diào)查”活動(dòng)。?點(diǎn)擊閱讀原文,填寫調(diào)查問卷,您將獲得價(jià)值299元的「2020 AI 開發(fā)者萬(wàn)人大會(huì)」在線直播門票一張喲~
推薦閱讀:為何你的 SaaS 想法總是失敗?沒想清楚這 4 個(gè)原因可能會(huì)繼續(xù)失敗! 如何給女朋友解釋什么是撞庫(kù)、脫庫(kù)和洗庫(kù)? 開源的未來 10 年:中國(guó)開源社區(qū)建立是關(guān)鍵 萬(wàn)字好文:智能合約編寫之Solidity的編程攻略,建議收藏! Python 爬取疫情期間全球股市走向,笑不出來...... 無(wú)代碼時(shí)代來臨,程序員如何保住飯碗? 真香,朕在看了!總結(jié)
以上是生活随笔為你收集整理的看完这一篇,你就对 Spring Security 略窥门径了 | 原力计划的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聚焦产业·城市、擎领数字未来:IMPAC
- 下一篇: 防水耐脏,超大容量双肩包,限时拼团仅需4