1 場景
1.1 說明
springSecurity作為一個權(quán)限管理系統(tǒng),在生產(chǎn)環(huán)境使用绒净,還是比較復(fù)雜的见咒,涉及的相關(guān)點比較多。網(wǎng)上文章里疯溺,并沒有比較全的配置论颅。
本文主要將springSecurity在生產(chǎn)環(huán)境中使用時,需要注意到的地方囱嫩,進行了相關(guān)整理恃疯。
springSecurity默認是基于session的非前后端分離
場景,本文基于此場景進行配置墨闲,有時間的時候今妄,后續(xù)會記錄補充如下場景:
(1)非前后端分離
(2)基于JWT的非前后端分離
(3)網(wǎng)關(guān)上整合權(quán)限控制
1.2 源碼
本文是基于各個應(yīng)用模塊單獨寫的配置,完整的測試代碼鸳碧,可關(guān)注盾鳞、點贊后私信博主。所有的代碼瞻离,博主均已校驗過腾仅,demo可正常跑通,可直接應(yīng)用到生產(chǎn)環(huán)境中套利。
1.3 版本
spring-boot版本:2.3.3.RELEASE
其他版本:
<!-- ==========【freemarker權(quán)限security標(biāo)簽支持】========== start -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- ==========【freemarker權(quán)限security標(biāo)簽支持】========== end -->
2 登錄
前提推励,屏蔽csrf防護鹤耍,否則系統(tǒng)登錄、登出無法正常訪問验辞。如需打開稿黄,需自己進行相關(guān)配置。
// 屏蔽csrf防護
http.csrf().disable();
2.1 自定義登錄頁面
本文前端頁面跌造,使用的freemarker
杆怕。
2.1.1 頁面配置
springSecurity登錄表單默認使用的是自己的頁面,一般系統(tǒng)都需要進行自定義登錄頁面
壳贪。
訪問系統(tǒng)頁面時陵珍,如未認證
,則自動跳轉(zhuǎn)到登錄頁面
撑碴。
文件路徑:resources\templates\system\main.ftlh
如下:
<form action="/doLogin" method="post">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" name="username" value="admin"></td>
</tr>
<tr>
<td>密碼:</td>
<td><input type="text" name="password" value="123456"></td>
</tr>
<tr>
<td><input type="submit" value="登錄"></td>
</tr>
</table>
</form>
如上代碼所示撑教,登錄時的相關(guān)參數(shù)如下:
參數(shù)描述 | 參數(shù) |
---|---|
加載登錄頁面路徑 | /initLogin |
提交登錄請求路徑 | /doLogin |
用戶名 | username |
密碼 | password |
2.1.2 后臺代碼配置
/**
* 加載登錄頁面
* @return
*/
@RequestMapping(value = {"initLogin"})
public ModelAndView initLogin(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("system/login");
return modelAndView;
}
備注:提交登錄請求路徑(/doLogin)
無需配置朝墩,此請求醉拓,會走security自己的認證流程。
2.1.3 登錄請求放行
放行“登錄頁面”收苏,“登錄請求”等相關(guān)權(quán)限驗證請求
http.authorizeRequests()
// ......
// 放行“登錄頁面”亿卤,“登錄請求”,“退出”等相關(guān)權(quán)限驗證請求
.antMatchers("/initLogin", "/doLogin", "/doLogout").permitAll()
// 任意請求需認證通過
.anyRequest().authenticated();
2.1.4 登錄參數(shù)配置
security的登錄請求路徑和參數(shù)都是默認配置的鹿霸,這里我們更改為自己的請求配置:
http.formLogin()
// 登錄時自定義“用戶”參數(shù)名(默認為:username)
.usernameParameter("username")
// 登錄時自定義“用戶”參數(shù)名(默認為:password)
.passwordParameter("password")
// 自定義登錄頁面(默認為:login/GET)
.loginPage("/initLogin")
// 自定義登錄請求路徑(默認為:login/POST)
.loginProcessingUrl("/doLogin")
2.2 登錄校驗邏輯
2.1 自定義登錄用戶對象
擴展security自帶的用戶對象(org.springframework.security.core.userdetails.User)排吴,擴展自定義屬性
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 自定義用戶對象
*/
public class LoginUser extends User {
/**
* 自定義用戶屬性
*/
private String departmentCode;
public LoginUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public String getDepartmentCode() {
return departmentCode;
}
public void setDepartmentCode(String departmentCode) {
this.departmentCode = departmentCode;
}
}
2.2 自定義用戶認證service
自定義用戶認證,主要是將用戶根據(jù)用戶名懦鼠,從數(shù)據(jù)庫中查詢出來钻哩。組裝好org.springframework.security.core.userdetails.UserDetails
的實現(xiàn)類對象,交由security進行驗證肛冶。后續(xù)security的驗證街氢,如未拋出異常,則認證通過睦袖,否則認證失敗珊肃,然后,可根據(jù)拋出的異常類型馅笙,來識別認證失敗的原因伦乔。
/**
* 自定義用戶認證service
*/
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Resource
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// ========== 【1】校驗參數(shù) ==========
if (StringUtils.isBlank(username)) {
throw new UsernameNotFoundException("用戶代碼為空");
}
// ========== 【2】查詢用戶信息 ==========
UserInfo userInfo = userService.getUserInfoByUserName(username);
if (userInfo == null) {
throw new UsernameNotFoundException("用戶名或密碼錯誤");
}
// 密碼
String password = userInfo.getPassword();
// 用戶其他信息(用戶部門代碼)
String departmentCode = userInfo.getDepartmentCode();
// ========== 【3】查詢授權(quán)信息 ==========
// 角色代碼和授權(quán)代碼,均在此列表中配置(角色代碼前加“ROLE_”董习,來和權(quán)限區(qū)分)
List<String> authorityList = new ArrayList<>();
// 初始化角色信息
List<String> roleCodeList = userService.getRoleCodeListByUserName(username);
if (CollectionUtils.isNotEmpty(roleCodeList)) {
for (String roleCode : roleCodeList) {
authorityList.add("ROLE_" + roleCode);
}
}
// 初始化權(quán)限信息
List<String> permissionCodeList = userService.getPermissionCodeListByUserName(username);
if (CollectionUtils.isNotEmpty(permissionCodeList)) {
authorityList.addAll(permissionCodeList);
}
// ========== 【4】組裝用戶信息 ==========
// 組裝通用信息
LoginUser loginUser = new LoginUser(username, password, AuthorityUtils.createAuthorityList(authorityList.toArray(new String[0])));
// 組裝自定義用戶信息
loginUser.setDepartmentCode(departmentCode);
return loginUser;
}
}
2.3 認證通過邏輯
認證通過后烈和,需進行頁面跳轉(zhuǎn),有兩種方式皿淋,一種是跳轉(zhuǎn)到訪問登錄頁面前的頁面(訪問某個頁面招刹,因為未認證虱颗,自動跳轉(zhuǎn)到登錄頁面,當(dāng)?shù)卿洺晒笳嵛梗詣犹D(zhuǎn)到此頁面忘渔,而不是系統(tǒng)主頁面),一種是跳轉(zhuǎn)到系統(tǒng)主頁面缰儿。
可根據(jù)實際業(yè)務(wù)需求選擇畦粮,這里選擇第一種。
一般登錄成功后乖阵,系統(tǒng)會執(zhí)行自定義邏輯宣赔,如記錄登錄IP、登錄時間等,這里使用自定義登錄成功邏輯
莫鸭。
2.3.1 自定義登錄成功處理器
/**
* 自定義登錄成功處理器
*/
@Component
public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
// TODO 自定義登錄成功邏輯......
System.out.println("自定義登錄成功邏輯......");
super.onAuthenticationSuccess(request, response, authentication);
}
}
2.3.2 配置登錄成功邏輯
- 注入bean
/**
* 自定義登錄成功邏輯
*/
@Autowired
private CustomLoginSuccessHandler customLoginSuccessHandler;
- 配置執(zhí)行器
http.formLogin()
// 自定義登錄成功forward路徑
//.successForwardUrl("/")
// 自定義登錄成功redirect路徑【默認】(登錄成功后粘勒,頁面重定向到跳轉(zhuǎn)登錄頁面前的頁面Referer)
//.defaultSuccessUrl("/")
// 自定義登錄成功邏輯(redirect主頁面+自定義業(yè)務(wù)邏輯)
.successHandler(customLoginSuccessHandler)
// 自定義登錄成功redirect路徑(登錄成功后,頁面重定向到設(shè)置的登錄成功頁面:"/")
//.defaultSuccessUrl("/", true)
2.3.3 登錄主頁面后臺代碼
/**
* 系統(tǒng)主頁面
* @return
*/
@RequestMapping(value = {"/"})
public ModelAndView main() {
ModelAndView modelAndView = new ModelAndView("system/main");
// 獲取當(dāng)前登錄人信息
LoginUser loginUser = SecurityUtils.getLoginUser();
modelAndView.addObject("loginUser", loginUser);
return modelAndView;
}
2.4 獲取認證信息
security認證通過后钩蚊,會將認證信息,保存在ThreadLocal中蹈矮,故可以通過其自帶的靜態(tài)方法獲扰槁摺:
SecurityContextHolder.getContext().getAuthentication()
此處可獲取2.2中封裝的自定義對象:LoginUser。
/**
* security工具類
*/
public class SecurityUtils {
/**
* 默認角色前綴
*/
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
/**
* 獲取認證信息
* @return
*/
public static Authentication getAuthentication() {
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext != null) {
return securityContext.getAuthentication();
}
return null;
}
/**
* 獲取當(dāng)前登錄用戶對象
* @return
*/
public static LoginUser getLoginUser() {
Authentication authentication = SecurityUtils.getAuthentication();
if (authentication != null) {
return (LoginUser) authentication.getPrincipal();
}
return null;
}
}
2.5 認證失敗邏輯
認證失敗時泛鸟,跳轉(zhuǎn)到登錄頁面蝠咆,并返回錯誤信息。
這里我們通過forward到認證失敗頁面(即登錄頁面)北滥,由于使用的是forward進行的跳轉(zhuǎn)刚操,故可以獲取request中的屬性WebAttributes.AUTHENTICATION_EXCEPTION
,來獲取異常信息再芋,來返回到前臺菊霜。
也可通過redirect到認證失敗頁面(登錄頁面),但是請求中無法獲取失敗異常信息WebAttributes.AUTHENTICATION_EXCEPTION祝闻。
可以考慮自定義認證失敗邏輯failureHandler
占卧,來實現(xiàn)此功能。
2.5.1 認證失敗后臺
/**
* 登錄失敗頁面
* @param request
* @return
*/
@RequestMapping(value = {"loginFail"})
public ModelAndView loginFail(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("system/login");
String error = null;
// 登錄異常處理
Object exception = request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if (exception instanceof AuthenticationException) {
if(exception instanceof UsernameNotFoundException){
// 自己拋出異常信息
error = ((UsernameNotFoundException) exception).getMessage();
}else if (exception instanceof BadCredentialsException){
error = "用戶名或者密碼輸入錯誤联喘,請重新輸入!";
}else if (exception instanceof LockedException){
error = "賬戶被鎖定华蜒,請聯(lián)系管理員!";
}else if (exception instanceof CredentialsExpiredException){
error = "密碼過期,請聯(lián)系管理員!";
}else if (exception instanceof AccountExpiredException){
error = "賬戶過期豁遭,請聯(lián)系管理員!";
}else if (exception instanceof DisabledException){
error = "賬戶被禁用叭喜,請聯(lián)系管理員!";
}else{
error = "認證失敗";
}
}
modelAndView.addObject("error", error);
return modelAndView;
}
2.5.2 認證失敗配置
http.formLogin()
// 自定義失敗forward路徑(默認為:loginPage + "?error")
.failureForwardUrl("/loginFail")
2 登出
2.1 頁面配置
<a href="/doLogout">退出</a>
2.2 后臺代碼
登出,走的是security的邏輯蓖谢,無需自己寫后臺代碼捂蕴。
2.3 登出請求放行
有可能執(zhí)行登出操作的時候譬涡,session已失效,因此登出系統(tǒng)也需要放行請求啥辨,不進行認證校驗
http.authorizeRequests()
// ......
// 放行“登錄頁面”涡匀,“登錄請求”,“退出”等相關(guān)權(quán)限驗證請求
.antMatchers("/initLogin", "/doLogin", "/doLogout").permitAll()
// 任意請求需認證通過
.anyRequest().authenticated();
2.4 自定義登出處理器
一般系統(tǒng)登出時溉知,也會進行相關(guān)業(yè)務(wù)操作陨瘩,如記錄日志,發(fā)送消息等级乍。
2.4.1 自定義登出成功處理器
/**
* 自定義登出成功處理器
**/
@Configuration
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// TODO 自定義登出成功邏輯......
System.out.println("自定義登出成功邏輯......");
super.onLogoutSuccess(request, response, authentication);
}
}
2.4.2 配置登錄成功邏輯
- 注入bean
/**
* 自定義登出成功邏輯
*/
@Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
- 配置執(zhí)行器
// ---------- [登出系統(tǒng)] ----------
http.logout()
// 刪除認證信息(默認為true)
.clearAuthentication(true)
// 退出系統(tǒng)時使session無效(默認為true)
.invalidateHttpSession(true)
// 退出系統(tǒng)時舌劳,自定義請求路徑
.logoutUrl("/doLogout")
// 自定義登出成功邏輯(redirect登錄頁面+自定義業(yè)務(wù)邏輯)
.logoutSuccessHandler(customLogoutSuccessHandler);
3 URL授權(quán)驗證
security可以對指定URL進行授權(quán)驗證判斷。個人認為主要是用來對某些特殊訪問請求進行專門的認證
(如api)玫荣,一般不會在此配置角色權(quán)限的驗證
甚淡。
3.1 驗證配置
// ---------- [基于表單的身份驗證] ----------
// 順序很重要,從上而下依次驗證
http.authorizeRequests()
// ---------- 自定義基于URL授權(quán)驗證[也可加http方式限制:(HttpMethod method, String... antPatterns)]
// 判斷是否有某權(quán)限
.antMatchers("/noPower/**").hasAuthority("noPower")
// 判斷是否有某角色
.antMatchers("/admin/**").hasRole("admin")
// 判斷是否有任一權(quán)限
.antMatchers("/user/list").hasAnyAuthority("user:list", "user:all")
// 自定義權(quán)限校驗(參數(shù)來自WebSecurityExpressionRoot屬性)
.antMatchers("/api/**").access("@customAccessForApi.hasPermission(request,authentication)")
// 放行“登錄”捅厂,“退出”等相關(guān)權(quán)限驗證請求
.antMatchers("/initLogin", "/doLogin", "/doLogout").permitAll()
// 任意請求需認證通過
.anyRequest().authenticated();
認證通過對請求進行攔截贯卦,如antMatchers
,可以使用多種認證方式:
使用
hasRole
對角色進行認證可以使用
hasAuthority
對權(quán)限認證恒傻。通過
自定義邏輯
對請求進行進行認證
3.2 自定義邏輯驗證
3.2.1 自定義驗證邏輯
/**
* 自定義權(quán)限監(jiān)測
*/
@Component
public class CustomAccessForApi {
/**
* 權(quán)限監(jiān)測
*/
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
if (StringUtils.startsWith(request.getRemoteAddr(), "192.168.56")) {
// 滿足條件的IP脸侥,可以訪問此接口
return true;
}
return false;
}
}
3.2.2 配置自定義驗證邏輯
http.authorizeRequests()
// 自定義權(quán)限校驗(參數(shù)來自WebSecurityExpressionRoot屬性)
.antMatchers("/api/**").access("@customAccessForApi.hasPermission(request,authentication)")
4 方法授權(quán)驗證
security可以對具體某個方法進行授權(quán)驗證判斷,一般加在Controller的對外請求方法上盈厘。
此方法,需要配置注解@EnableGlobalMethodSecurity
開啟方法前后權(quán)限判斷官边,一般使用@PreAuthorize
進行方法執(zhí)行前判斷
// 開啟全局“方法安全”控制(開啟方法前后權(quán)限判斷沸手,一般使用@PreAuthorize進行方法執(zhí)行前判斷)
@EnableGlobalMethodSecurity(prePostEnabled = true)
4.1 判斷是否有角色
@PreAuthorize("hasRole('admin')")
@ResponseBody
@RequestMapping("add")
public String add() {
return "department add ...";
}
4.2 判斷是否有權(quán)限
@PreAuthorize("hasAuthority('department:list')")
@RequestMapping("list")
public ModelAndView list() {
return new ModelAndView("department/department_list");
}
4.3 自定義權(quán)限判斷
4.3.1 自定義校驗器
@Configuration
public class CustomAccessForDepartmentDelete {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
return false;
}
}
4.3.2 配置自定義校驗器
// 自定義權(quán)限控制,參數(shù)前需加#
@PreAuthorize("@customAccessForDepartmentDelete.hasPermission(#request,#authentication)")
@ResponseBody
@RequestMapping("delete")
public String delete() {
return "department delete ...";
}
5 前端頁面授權(quán)驗證
security也可以在前端頁面注簿,加標(biāo)簽契吉,來控制頁面元素的展示。這里前端使用的是freemarker诡渴,需要額外做些配置捐晶,才可以使用。
5.1 freemarker配置security標(biāo)簽
5.1.1 maven依賴
<!-- ==========【freemarker權(quán)限security標(biāo)簽支持】========== start -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- ==========【freemarker權(quán)限security標(biāo)簽支持】========== end -->
5.1.2 拷貝tld文件
將spring-security-taglibs/META-INF/security.tld
妄辩,拷貝到拷貝到resource的目錄tags中惑灵。
5.1.3 配置
/**
* Freemarker的Security標(biāo)簽支持
*/
@Configuration
public class FreemarkerSecurityTaglibConfig{
/**
* security標(biāo)簽路徑(來自"spring-security-taglibs/META-INF/security.tld")<br>
* 此文件需拷貝到resource的目錄tags中
*/
private static final String SECURITY_TLD_PATH="/tags/security.tld";
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@PostConstruct
public void freeMarkerConfigurer() {
List<String> classpathTlds = new ArrayList<>();
classpathTlds.add(SECURITY_TLD_PATH);
freeMarkerConfigurer.getTaglibFactory().setClasspathTlds(classpathTlds);
}
}
5.2 引入標(biāo)簽
freemarker前臺文件ftlh,文件頭眼耀,引入標(biāo)簽:
<#assign security=JspTaglibs["http://www.springframework.org/security/tags"] />
5.3 使用
5.3.1 判斷有無角色
<@security.authorize access="hasRole('admin')">
<a href="/department/test">test按鈕</a>
</@security.authorize>
5.3.2 判斷有無權(quán)限
<@security.authorize access="hasAuthority('department:test')">
<a href="/department/test">test按鈕</a>
</@security.authorize>
6 api驗證授權(quán)
有時候英支,我們希望在java代碼中直接判斷有無某角色、有無某權(quán)限哮伟。作者對此進行了代碼封裝干花,可通過靜態(tài)方法進行判斷妄帘。
此判斷,暫不支持權(quán)限繼承池凄。
6.1 封裝
/**
* security工具類
*/
public class SecurityUtils {
/**
* 默認角色前綴
*/
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
/**
* 獲取認證信息
* @return
*/
public static Authentication getAuthentication() {
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext != null) {
return securityContext.getAuthentication();
}
return null;
}
/**
* 判斷有某權(quán)限(暫不支持權(quán)限繼承)
* @return
*/
public static boolean hasAuthority(String authority) {
if (StringUtils.isNotEmpty(authority)) {
return SecurityUtils.hasAnyAuthorityName(null, authority);
}
return false;
}
/**
* 判斷有任一權(quán)限(暫不支持權(quán)限繼承)
* @param authorityArr
* @return
*/
public static boolean hasAnyAuthority(String... authorityArr) {
if (authorityArr != null && authorityArr.length > 0) {
return SecurityUtils.hasAnyAuthorityName(null, authorityArr);
}
return false;
}
/**
* 判斷有某角色(暫不支持權(quán)限繼承)
* @param role
* @return
*/
public static boolean hasRole(String role) {
if (StringUtils.isNotEmpty(role)) {
return SecurityUtils.hasAnyAuthorityName(DEFAULT_ROLE_PREFIX, role);
}
return false;
}
/**
* 判斷有任一角色(暫不支持權(quán)限繼承)
* @param roleArr
* @return
*/
public static boolean hasAnyRole(String... roleArr) {
if (roleArr != null && roleArr.length > 0) {
return SecurityUtils.hasAnyAuthorityName(DEFAULT_ROLE_PREFIX, roleArr);
}
return false;
}
/**
* 判斷是否滿足通用權(quán)限信息抡驼,有一個滿足即為滿足(包括角色和權(quán)限)
* @param prefix 前綴
* @param authorityNames 權(quán)限名稱
* @return
*/
private static boolean hasAnyAuthorityName(String prefix, String... authorityNames) {
if (authorityNames != null && authorityNames.length > 0) {
Set<String> authoritySet = SecurityUtils.getAuthoritySet();
if (CollectionUtils.isNotEmpty(authoritySet)) {
for (String authorityName : authorityNames) {
String defaultedRole = getRoleWithDefaultPrefix(prefix, authorityName);
if (authoritySet.contains(defaultedRole)) {
return true;
}
}
}
}
return false;
}
/**
* 獲取當(dāng)前用戶權(quán)限集合信息
* @return
*/
private static Set<String> getAuthoritySet() {
Authentication authentication = SecurityUtils.getAuthentication();
if (authentication != null) {
Collection<? extends GrantedAuthority> userAuthorities = authentication.getAuthorities();
return AuthorityUtils.authorityListToSet(userAuthorities);
}
return null;
}
/**
* 如果defaultRolePrefix為非空且role的開頭不是defaultRolePrefix,則使用defaultRolePrefix前綴角色肿仑。
* @param defaultRolePrefix
* @param role
* @return
*/
private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
if (role == null) {
return role;
}
if (defaultRolePrefix == null || defaultRolePrefix.length() == 0) {
return role;
}
if (role.startsWith(defaultRolePrefix)) {
return role;
}
return defaultRolePrefix + role;
}
}
6.2 使用
java代碼中直接使用靜態(tài)方法判斷即可婶恼,如下:
// 判斷有角色
SecurityUtils.hasRole("admin");
// 判斷有任一角色
SecurityUtils.hasAnyRole("admin","user");
// 判斷有權(quán)限
SecurityUtils.hasAuthority("user:add");
// 判斷有任一權(quán)限
SecurityUtils.hasAnyAuthority("user:add","user:edit");
7 自定義授權(quán)驗證失敗返回方式
當(dāng)系統(tǒng)訪問認知失敗時,默認返回授權(quán)驗證的錯誤頁面柏副,這種方式對于ajax的請求勾邦,十分不友好。
因此我們希望在驗證授權(quán)失敗時割择,如果是json請求眷篇,則返回json格式的錯誤信息,如果是其他請求荔泳,則返回錯誤頁面蕉饼。
7.1 前臺頁面
授權(quán)驗證失敗頁面accessDenied.ftlh
<body>
無訪問權(quán)限
</body>
7.2 后臺代碼
/**
* 無訪問權(quán)限頁面
* @return
*/
@RequestMapping("accessDenied")
public ModelAndView accessDenied() {
return new ModelAndView("system/accessDenied");
}
7.3 配置
// ---------- [異常處理]ExceptionTranslationFilter ----------
http.exceptionHandling()
// 認證失敗(不使用默認的表單form登錄認證時玛歌,可使用此方式)
//.authenticationEntryPoint((request, response, authException) -> {
//})
// 訪問拒絕句柄(認證通過后昧港,無操作權(quán)限時)
.accessDeniedHandler((request, response, accessDeniedException) -> {
String contentType = request.getHeader("content-type");
boolean jsonRequestFlag = (contentType != null && contentType.contains("json"));
// 判斷是否是json請求
if (jsonRequestFlag) {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json;charset=UTF-8");
Writer writer = response.getWriter();
JSONObject json = new JSONObject();
json.put("success", false);
json.put("message", "無操作權(quán)限:" + accessDeniedException.getMessage());
writer.write(json.toJSONString());
writer.flush();
writer.close();
} else {
request.getRequestDispatcher("/accessDenied").forward(request, response);
}
});
8 分布式session共享
可使用spring-session或tomcat-redis-session-manager。后續(xù)補充