?
?在我們做SpringBoot項(xiàng)目的時(shí)候,認(rèn)證授權(quán)是必不可少的功能!我們經(jīng)常會(huì)選擇Shiro、Spring Security這類權(quán)限認(rèn)證框架來(lái)實(shí)現(xiàn),但這些框架使用起來(lái)有點(diǎn)繁瑣,而且功能也不夠強(qiáng)大。最近發(fā)現(xiàn)一款功能強(qiáng)大的權(quán)限認(rèn)證框架Sa-Token,它使用簡(jiǎn)單、API設(shè)計(jì)優(yōu)雅,推薦給大家!
?
Sa-Token簡(jiǎn)介
Sa-Token是一款輕量級(jí)的Java權(quán)限認(rèn)證框架,可以用來(lái)解決登錄認(rèn)證、權(quán)限認(rèn)證、Session會(huì)話、單點(diǎn)登錄、OAuth2.0、微服務(wù)網(wǎng)關(guān)鑒權(quán)等一系列權(quán)限相關(guān)問(wèn)題。
框架集成簡(jiǎn)單、開(kāi)箱即用、API設(shè)計(jì)優(yōu)雅,通過(guò)Sa-Token,你將以一種極其簡(jiǎn)單的方式實(shí)現(xiàn)系統(tǒng)的權(quán)限認(rèn)證部分,有時(shí)候往往只需一行代碼就能實(shí)現(xiàn)功能。
Sa-Token功能很全,具體可以參考下圖。
?
使用
在SpringBoot中使用Sa-Token是非常簡(jiǎn)單的,接下來(lái)我們使用它來(lái)實(shí)現(xiàn)最常用的認(rèn)證授權(quán)功能,包括登錄認(rèn)證、角色認(rèn)證和權(quán)限認(rèn)證。
集成及配置
Sa-Token的集成和配置都非常簡(jiǎn)單,不愧為開(kāi)箱即用。
<!--?Sa-Token?權(quán)限認(rèn)證?-->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.24.0</version>
</dependency>
#?Sa-Token配置
sa-token:#?token名稱?(同時(shí)也是cookie名稱)token-name:?Authorization#?token有效期,單位秒,-1代表永不過(guò)期timeout:?2592000#?token臨時(shí)有效期?(指定時(shí)間內(nèi)無(wú)操作就視為token過(guò)期),單位秒activity-timeout:?-1#?是否允許同一賬號(hào)并發(fā)登錄?(為false時(shí)新登錄擠掉舊登錄)is-concurrent:?true#?在多人登錄同一賬號(hào)時(shí),是否共用一個(gè)token?(為false時(shí)每次登錄新建一個(gè)token)is-share:?false#?token風(fēng)格token-style:?uuid#?是否輸出操作日志is-log:?false#?是否從cookie中讀取tokenis-read-cookie:?false#?是否從head中讀取tokenis-read-head:?true
登錄認(rèn)證
在管理系統(tǒng)中,除了登錄接口,基本都需要登錄認(rèn)證,在Sa-Token中使用路由攔截鑒權(quán)是最方便的,下面我們來(lái)實(shí)現(xiàn)下。
/***?后臺(tái)用戶管理*?Created?by?macro?on?2018/4/26.*/
@Controller
@Api(tags?=?"UmsAdminController",?description?=?"后臺(tái)用戶管理")
@RequestMapping("/admin")
public?class?UmsAdminController?{@Autowiredprivate?UmsAdminService?adminService;@ApiOperation(value?=?"登錄以后返回token")@RequestMapping(value?=?"/login",?method?=?RequestMethod.POST)@ResponseBodypublic?CommonResult?login(@RequestParam?String?username,?@RequestParam?String?password)?{SaTokenInfo?saTokenInfo?=?adminService.login(username,?password);if?(saTokenInfo?==?null)?{return?CommonResult.validateFailed("用戶名或密碼錯(cuò)誤");}Map<String,?String>?tokenMap?=?new?HashMap<>();tokenMap.put("token",?saTokenInfo.getTokenValue());tokenMap.put("tokenHead",?saTokenInfo.getTokenName());return?CommonResult.success(tokenMap);}
}
/***?Created?by?macro?on?2020/10/15.*/
@Slf4j
@Service
public?class?UmsAdminServiceImpl?implements?UmsAdminService?{@Overridepublic?SaTokenInfo?login(String?username,?String?password)?{SaTokenInfo?saTokenInfo?=?null;AdminUser?adminUser?=?getAdminByUsername(username);if?(adminUser?==?null)?{return?null;}if?(!SaSecureUtil.md5(password).equals(adminUser.getPassword()))?{return?null;}//?密碼校驗(yàn)成功后登錄,一行代碼實(shí)現(xiàn)登錄StpUtil.login(adminUser.getId());//?獲取當(dāng)前登錄用戶Token信息saTokenInfo?=?StpUtil.getTokenInfo();return?saTokenInfo;}
}
/***?Created?by?macro?on?2020/10/15.*/
@Slf4j
@Service
public?class?UmsAdminServiceImpl?implements?UmsAdminService?{@ApiOperation(value?=?"查詢當(dāng)前登錄狀態(tài)")@RequestMapping(value?=?"/isLogin",?method?=?RequestMethod.GET)@ResponseBodypublic?CommonResult?isLogin()?{return?CommonResult.success(StpUtil.isLogin());}
}
/***?Sa-Token相關(guān)配置*/
@Configuration
public?class?SaTokenConfig?implements?WebMvcConfigurer?{@Autowiredprivate?IgnoreUrlsConfig?ignoreUrlsConfig;/***?注冊(cè)sa-token攔截器*/@Overridepublic?void?addInterceptors(InterceptorRegistry?registry)?{registry.addInterceptor(new?SaRouteInterceptor((req,?resp,?handler)?->?{//?獲取配置文件中的白名單路徑List<String>?ignoreUrls?=?ignoreUrlsConfig.getUrls();//?登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證SaRouter.match(Collections.singletonList("/**"),?ignoreUrls,?StpUtil::checkLogin);})).addPathPatterns("/**");}
}
#?訪問(wèn)白名單路徑
secure:ignored:urls:-?/-?/swagger-ui/-?/*.html-?/favicon.ico-?/**/*.html-?/**/*.css-?/**/*.js-?/swagger-resources/**-?/v2/api-docs/**-?/actuator/**-?/admin/login-?/admin/isLogin
/***?全局異常處理*?Created?by?macro?on?2020/2/27.*/
@ControllerAdvice
public?class?GlobalExceptionHandler?{/***?處理未登錄的異常*/@ResponseBody@ExceptionHandler(value?=?NotLoginException.class)public?CommonResult?handleNotLoginException(NotLoginException?e)?{return?CommonResult.unauthorized(e.getMessage());}
}
角色認(rèn)證
角色認(rèn)證也就是我們定義好一套規(guī)則,比如ROLE-ADMIN角色可以訪問(wèn)/brand下的所有資源,而ROLE_USER角色只能訪問(wèn)/brand/listAll,接下來(lái)我們來(lái)實(shí)現(xiàn)下角色認(rèn)證。
/***?自定義權(quán)限驗(yàn)證接口擴(kuò)展*/
@Component
public?class?StpInterfaceImpl?implements?StpInterface?{@Autowiredprivate?UmsAdminService?adminService;@Overridepublic?List<String>?getPermissionList(Object?loginId,?String?loginType)?{AdminUser?adminUser?=?adminService.getAdminById(Convert.toLong(loginId));return?adminUser.getRole().getPermissionList();}@Overridepublic?List<String>?getRoleList(Object?loginId,?String?loginType)?{AdminUser?adminUser?=?adminService.getAdminById(Convert.toLong(loginId));return?Collections.singletonList(adminUser.getRole().getName());}
}
/***?Sa-Token相關(guān)配置*/
@Configuration
public?class?SaTokenConfig?implements?WebMvcConfigurer?{@Autowiredprivate?IgnoreUrlsConfig?ignoreUrlsConfig;/***?注冊(cè)sa-token攔截器*/@Overridepublic?void?addInterceptors(InterceptorRegistry?registry)?{registry.addInterceptor(new?SaRouteInterceptor((req,?resp,?handler)?->?{//?獲取配置文件中的白名單路徑List<String>?ignoreUrls?=?ignoreUrlsConfig.getUrls();//?登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證SaRouter.match(Collections.singletonList("/**"),?ignoreUrls,?StpUtil::checkLogin);//?角色認(rèn)證:ROLE_ADMIN可以訪問(wèn)所有接口,ROLE_USER只能訪問(wèn)查詢?nèi)拷涌赟aRouter.match("/brand/listAll",?()?->?{StpUtil.checkRoleOr("ROLE_ADMIN","ROLE_USER");//強(qiáng)制退出匹配鏈SaRouter.stop();});SaRouter.match("/brand/**",?()?->?StpUtil.checkRole("ROLE_ADMIN"));})).addPathPatterns("/**");}
}
/***?全局異常處理*?Created?by?macro?on?2020/2/27.*/
@ControllerAdvice
public?class?GlobalExceptionHandler?{/***?處理沒(méi)有角色的異常*/@ResponseBody@ExceptionHandler(value?=?NotRoleException.class)public?CommonResult?handleNotRoleException(NotRoleException?e)?{return?CommonResult.forbidden(e.getMessage());}
}
權(quán)限認(rèn)證
當(dāng)我們給角色分配好權(quán)限,然后給用戶分配好角色后,用戶就擁有了這些權(quán)限。我們可以為每個(gè)接口分配不同的權(quán)限,擁有該權(quán)限的用戶就可以訪問(wèn)該接口。這就是權(quán)限認(rèn)證,接下來(lái)我們來(lái)實(shí)現(xiàn)下它。
/***?Sa-Token相關(guān)配置*/
@Configuration
public?class?SaTokenConfig?implements?WebMvcConfigurer?{@Autowiredprivate?IgnoreUrlsConfig?ignoreUrlsConfig;/***?注冊(cè)sa-token攔截器*/@Overridepublic?void?addInterceptors(InterceptorRegistry?registry)?{registry.addInterceptor(new?SaRouteInterceptor((req,?resp,?handler)?->?{//?獲取配置文件中的白名單路徑List<String>?ignoreUrls?=?ignoreUrlsConfig.getUrls();//?登錄認(rèn)證:除白名單路徑外均需要登錄認(rèn)證SaRouter.match(Collections.singletonList("/**"),?ignoreUrls,?StpUtil::checkLogin);//?權(quán)限認(rèn)證:不同接口, 校驗(yàn)不同權(quán)限SaRouter.match("/brand/listAll",?()?->?StpUtil.checkPermission("brand:read"));SaRouter.match("/brand/create",?()?->?StpUtil.checkPermission("brand:create"));SaRouter.match("/brand/update/{id}",?()?->?StpUtil.checkPermission("brand:update"));SaRouter.match("/brand/delete/{id}",?()?->?StpUtil.checkPermission("brand:delete"));SaRouter.match("/brand/list",?()?->?StpUtil.checkPermission("brand:read"));SaRouter.match("/brand/{id}",?()?->?StpUtil.checkPermission("brand:read"));})).addPathPatterns("/**");}
}
/***?全局異常處理*?Created?by?macro?on?2020/2/27.*/
@ControllerAdvice
public?class?GlobalExceptionHandler?{/***?處理沒(méi)有權(quán)限的異常*/@ResponseBody@ExceptionHandler(value?=?NotPermissionException.class)public?CommonResult?handleNotPermissionException(NotPermissionException?e)?{return?CommonResult.forbidden(e.getMessage());}
}
?
總結(jié)
通過(guò)對(duì)Sa-Token的一波實(shí)踐,我們可以發(fā)現(xiàn)它的API設(shè)計(jì)非常優(yōu)雅,比起Shiro和Spring Security來(lái)說(shuō)確實(shí)順手多了。Sa-Token不僅提供了一系列強(qiáng)大的權(quán)限相關(guān)功能,還提供了很多標(biāo)準(zhǔn)的解決方案,比如Oauth2、分布式Session會(huì)話等,大家感興趣的話可以研究下。
?
參考資料
Sa-Token的官方文檔很全,也很良心,不僅提供了解決方式,還提供了解決思路,強(qiáng)烈建議大家去看下。
官方文檔:http://sa-token.dev33.cn/
有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
?
?
總結(jié)
以上是生活随笔為你收集整理的再见Spring Security!推荐一款功能强大的权限认证框架,用起来够优雅!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。