我们在做SpringBoot项目的时候,认证授权是必不可少的功能!我们经常会选择Shiro、SpringSecurity等权限认证框架来实现,但是这些框架使用起来有点麻烦,功能也不够强大。最近发现一个强大的权限认证框架Sa-Token,简单易用,API设计优雅。我把它推荐给了每一个人!SpringBoot实战电商项目商城(50k+star)地址:https://github.com/macrozheng/mallSa-Token介绍Sa-Token是一个轻量级的Java权限认证框架,可以用来解决登录认证、权限认证、Session会话、单点登录、OAuth2.0、微服务网关认证等一系列权限相关的问题。框架易于集成,开箱即用,API设计优雅。通过Sa-Token,您将以极其简单的方式实现系统的权限认证部分,有时仅需一行代码即可实现功能。Sa-Token功能齐全,详见下图。在SpringBoot中使用Sa-Token非常简单。接下来我们用它来实现最常用的认证授权功能,包括登录认证、角色认证和权限认证。集成与配置Sa-Token的集成与配置非常简单,值得开箱即用。首先,我们需要在项目的pom.xml中添加Sa-Token相关依赖;cn.dev33sa-token-spring-boot-starter1.24.0然后在application.yml中添加Sa-Token的相关配置,考虑到支持前后端分离的项目,我们关闭cookie从中读取token,改为从head中读取token.#Sa-Token配置sa-token:#token名称(也是cookie名称)token-name:Authorization#token有效期,以秒为单位,-1表示永不过期timeout:2592000#token临时有效期(指定时间内没有操作time视为token过期),单位秒activity-timeout:-1#是否允许同一个账号并发登录(为false时,新登录挤掉旧登录)is-concurrent:true#当多人登录同一个账号,是否共享一个token(为false时,每次登录都创建一个新的token)is-share:false#tokenstyletoken-style:uuid#是否输出操作日志is-log:false#是否从cookie中读取tokenis-read-cookie:false#是否从head中读取tokenis-read-head:true登录认证在管理系统中,除了登录界面,基本上需要登录身份验证。在Sa-Token中使用路由拦截认证最为方便。下面我们来实现一下。实现登录认证很简单,首先在UmsAdminController中添加一个登录接口;/***后台用户管理*macro于2018/4/26创建。*/@Controller@Api(tags="UmsAdminController",description="后台用户管理")@RequestMapping("/admin")publicclassUmsAdminController{@AutowiredprivateUmsAdminServiceadminService;@ApiOperation(value="登录后返回令牌")@RequestMapping(value="/login",method=RequestMethod.POST)@ResponseBodypublicCommonResultlogin(@RequestParamStringusername,@RequestParamStringpassword){SaTokenInfosaTokenInfo=adminService.登录(用户名,密码);if(saTokenInfo==null){returnCommonResult.validateFailed("用户名或密码错误");}MaptokenMap=newHashMap<>();tokenMap.put("令牌",saTokenInfo.getTokenValue());tokenMap.put("tokenHead",saTokenInfo.getTokenName());返回通用结果。成功(tokenMap);}}然后在UmsAdminServiceImpl中加入登录的具体逻辑,先验证密码,然后调用StpUtil.login(adminUser.getId())可以用来登录,一行调用API即可;/***由宏创建于2020/10/15。*/@Slf4j@ServicepublicclassUmsAdminServiceImplimplementsUmsAdminService{@OverridepublicSaTokenInfologin(Stringusername,Stringpassword){SaTokenInfosaTokenInfo=null;AdminUseradminUser=getAdminByUsername(用户名);如果(adminUser==null){返回null;}if(!SaSecureUtil.md5(password).equals(adminUser.getPassword())){返回null;}//验证密码成功后登录,一行代码实现登录StpUtil.login(adminUser.getId());//获取当前登录用户的Token信息saTokenInfo=StpUtil.getTokenInfo();返回saTokenInfo;}}我们来添加一个测试接口,查询当前登录状态,返回true表示已经登录;/***由宏创建于2020/10/15。*/@Slf4j@ServicepublicclassUmsAdminServiceImplimplementsUmsAdminService{@ApiOperation(value="querycurrentLoginStatus")@RequestMapping(value="/isLogin",method=RequestMethod.GET)@ResponseBodypublicCommonResultisLogin(){返回CommonResult.success(StpUtil.isLogin());}}之后就可以通过Swagger访问登录界面获取Token了。账号为admin:123456,访问地址为:http://localhost:8088/swagger...然后将获取到的token添加到Authorization请求头中;访问/admin/isLogin接口,data属性会返回true,表示已经登录;接下来,我们需要添加除登录接口之外的所有接口添加登录认证,添加SaTokenJava配置类SaTokenConfig,注册一个路由拦截器SaRouteInterceptor,这里我们的IgnoreUrlsConfig配置会从配置文件中读取白名单配置;/***Sa-Token相关配置*/@ConfigurationpublicclassSaTokenConfigimplementsWebMvcConfigurer{@AutowiredprivateIgnoreUrlsConfigignoreUrlsConfig;/***注册sa-token拦截器*/@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newSaRouteInterceptor((req,resp,h)//获取配置文件中的白名单路径ListignoreUrls=ignoreUrlsConfig.getUrls();//登录鉴权:除白名单路径外需要登录鉴权SaRouter.match(Collections.singletonList("/**"),ignoreUrls,StpUtil::checkLogin);})).addPathPatterns("/**");}}application.yml文件中的白名单配置如下,注意打开Swagger的访问路径和静态资源路径;#访问白名单路径secure:ignored:urls:-/-/swagger-ui/-/*.html-/favicon.ico-/**/*.html-/**/*.css-/**/*.js-/swagger-resources/**-/v2/api-docs/**-/actuator/**-/admin/login-/admin/isLoginSa-Token会因为没有登录访问接口而抛出NotLoginExceptionin,所以我们需要全局处理;/***全局异常处理*宏创建于2020/2/27。*/@ControllerAdvicepublicclassGlobalExceptionHandler{/***处理非登录异常*/@ResponseBody@ExceptionHandler(value=NotLoginException.class)publicCommonResulthandleNotLoginException(NotLoginExceptione){returnAfterCommonResult.unauthorized(e.getMessage());}},我们在登录状态下访问接口,就可以获取到数据;当我们没有登录的时候(没有token),我们不能正常访问接口,返回码是401roleAuthentication角色认证的意思是我们定义了一套规则。例如ROLE-ADMIN角色可以访问/brand下的所有资源,而ROLE_USER角色只能访问/brand/listAll。接下来,我们将实现角色认证。首先,我们需要扩展Sa-Token的StpInterface接口,通过实现该方法返回用户的角色码和权限码;/***自定义权限验证接口扩展*/@ComponentpublicclassStpInterfaceImplimplementsStpInterface{@AutowiredprivateUmsAdminServiceadminService;@OverridepublicListgetPermissionList(ObjectloginId,StringloginType){AdminUseradminUser=adminService.getAdminById(Convert.toLong(loginId));返回adminUser.getRole().getPermissionList();}@OverridepublicListgetRoleList(ObjectloginId,StringloginType){AdminUseradminUser=adminService.getAdminById(Convert.toLong(loginId));返回Collections.singletonList(adminUser.getRole().getName());}}然后在Sa-Token的拦截器中配置路由规则,ROLE_ADMIN角色可以访问所有路径,而ROLE_USER只能访问/brand/listAll路径;/***Sa-Token相关配置*/@ConfigurationpublicclassSaTokenConfigimplementsWebMvcConfigurer{@AutowiredprivateIgnoreUrlsConfigignoreUrlsConfig;/***注册sa-token拦截器*/@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newSaRouteInterceptor((req,resp,handler)->{//获取配置文件中的白名单路径ListignoreUrls=ignoreUrlsConfig.getUrls();//登录Authentication:除了白名单路径需要登录认证SaRouter.match(Collections.singletonList("/**"),ignoreUrls,StpUtil::checkLogin);//角色认证:ROLE_ADMIN可以访问所有接口,ROLE_USER只能访问查询AllinterfacesSaRouter.match("/brand/listAll",()->{StpUtil.checkRoleOr("ROLE_ADMIN","ROLE_USER");//强制退出匹配链SaRouter.stop();});SaRouter.match("/brand/**",()->StpUtil.checkRole("ROLE_ADMIN"));})).addPathPatterns("/**");}}当不允许用户访问角色时,会抛出Sa-TokenNotRoleException,我们可以全局处理;/***全局异常处理*宏创建于2020/2/27。*/@ControllerAdvicepublicclassGlobalExceptionHandler{/***处理没有角色的异常*/@ResponseBody@ExceptionHandler(value=NotRoleException.class)publicCommonResulthandleNotRoleException(NotRoleExceptione){returnCommonResult.forbidden(e.getMessage());}}我们现在有两个用户,admin用户具有ROLE_ADMIN角色,macro用户具有ROLE_USER角色;使用admin账号访问/brand/list界面可以正常访问;使用宏账号访问/brand/list接口无法正常访问,返回码为403。我们可以给每个接口分配不同的权限,拥有这个权限的用户就可以访问该接口。这就是权限认证,接下来我们来实现。我们可以在Sa-Token拦截器中配置路由规则。admin用户可以访问所有路径,而macro用户只有读取权限,没有写入、修改、删除权限;/***Sa-Token相关配置*/@ConfigurationpublicclassSaTokenConfigimplementsWebMvcConfigurer{@AutowiredprivateIgnoreUrlsConfigignoreUrlsConfig;/***注册sa-token拦截器*/@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(new,SaRoutereqInterceptor,repceptor)->{//获取配置文件中的白名单路径ListignoreUrls=ignoreUrlsConfig.getUrls();//登录鉴权:除白名单路径外均需登录鉴权SaRouter.match(Collections.singletonList("/**"),ignoreUrls,StpUtil::checkLogin);//权限认证:不同的接口,验证不同的权限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("/**");}}当用户无权限访问时,Sa-Token会抛出NotPermissionException异常,我们可以全局处理;/***全局异常处理*宏创建于2020/2/27。*/@ControllerAdvicepublicclassGlobalExceptionHandler{/***未经许可处理异常*/@ResponseBody@ExceptionHandler(value=NotPermissionException.class)publicCommonResulthandleNotPermissionException(NotPermissionExceptione){returnCommonResult.forbidden(e.getMessage());}}使用admin账号访问/brand/可以正常访问删除界面;使用宏账号访问/brand/delete无法正常访问,返回码为403总结通过对Sa-Token的一波实践,我们可以发现它的API设计非常优雅,相对于Shiro和SpringSecurity来说这更容易。Sa-Token不仅提供了一系列强大的权限相关功能,还提供了很多标准的解决方案,比如Oauth2、分布式Session会话等,有兴趣的可以研究一下。参考资料Sa-Token的官方文档非常全面和认真。它不仅提供解决方案,而且提供解决方案。我强烈建议你看一看。官方文档:http://sa-token.dev33.cn/项目源码地址https://github.com/macrozheng...本文GitHubhttps://github.com/macrozheng/mall-learning收录,欢迎大家Star!