When we are working on the SpringBoot project, authentication and authorization is an indispensable function! We often choose permission authentication frameworks such as Shiro and Spring Security to implement, but these frameworks are a bit cumbersome to use, and their functions are not powerful enough. I recently discovered a powerful authorization authentication framework, Sa-Token, which is simple to use and elegant in API design. I recommend it to everyone!
SpringBoot actual combat e-commerce project mall (50k+star) address: https://github.com/macrozheng/mall
Introduction to Sa-Token
Sa-Token is a lightweight Java permission authentication framework that can be used to solve a series of permission-related problems such as login authentication, permission authentication, Session session, single sign-on, OAuth2.0, microservice gateway authentication and so on.
The framework is simple to integrate, out-of-the-box, and API design is elegant. With Sa-Token, you will implement the authorization authentication part of the system in an extremely simple way, and sometimes only one line of code is needed to achieve the function.
Sa-Token has very complete functions, please refer to the figure below for details.
use
It is very simple to use Sa-Token in SpringBoot. Next, we will use it to implement the most commonly used authentication and authorization functions, including login authentication, role authentication, and permission authentication.
Integration and configuration
The integration and configuration of Sa-Token are very simple, and it is worthy of being used out of the box.
- First, we need to add Sa-Token related dependencies
pom.xml
<!-- Sa-Token 权限认证 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
- Then
application.yml
. Considering to support front-end and back-end separation projects, we turn off reading token from cookie and read token from head instead.
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# token有效期,单位秒,-1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期),单位秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个token (为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
# 是否从cookie中读取token
is-read-cookie: false
# 是否从head中读取token
is-read-head: true
Login authentication
In the management system, in addition to the login interface, login authentication is basically required. It is the most convenient to use route interception authentication in Sa-Token. Let's implement it below.
- Realizing login authentication is very simple, first add a login interface
UmsAdminController
/**
* 后台用户管理
* Created by macro on 2018/4/26.
*/
@Controller
@Api(tags = "UmsAdminController", description = "后台用户管理")
@RequestMapping("/admin")
public class UmsAdminController {
@Autowired
private UmsAdminService adminService;
@ApiOperation(value = "登录以后返回token")
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public CommonResult login(@RequestParam String username, @RequestParam String password) {
SaTokenInfo saTokenInfo = adminService.login(username, password);
if (saTokenInfo == null) {
return CommonResult.validateFailed("用户名或密码错误");
}
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", saTokenInfo.getTokenValue());
tokenMap.put("tokenHead", saTokenInfo.getTokenName());
return CommonResult.success(tokenMap);
}
}
- Then
UmsAdminServiceImpl
, first verify the password, and then callStpUtil.login(adminUser.getId())
to achieve login, call the API one line to get it;
/**
* Created by macro on 2020/10/15.
*/
@Slf4j
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public 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;
}
// 密码校验成功后登录,一行代码实现登录
StpUtil.login(adminUser.getId());
// 获取当前登录用户Token信息
saTokenInfo = StpUtil.getTokenInfo();
return saTokenInfo;
}
}
- We add another test interface to query the current login status, and return
true
indicate that it has been logged in;
/**
* Created by macro on 2020/10/15.
*/
@Slf4j
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@ApiOperation(value = "查询当前登录状态")
@RequestMapping(value = "/isLogin", method = RequestMethod.GET)
@ResponseBody
public CommonResult isLogin() {
return CommonResult.success(StpUtil.isLogin());
}
}
- After that, you can access the login interface through Swagger to obtain the Token. The account is
admin:123456
and the access address is: http://localhost:8088/swagger-ui/
- Then add the obtained token to the
Authorization
- Access the
/admin/isLogin
interface, thedata
attribute will returntrue
, indicating that you are already logged in;
- Next, we need to add login authentication to all interfaces except the login interface, add the Sa-Token Java configuration class
SaTokenConfig
, register a route interceptorSaRouteInterceptor
, here ourIgnoreUrlsConfig
configuration will read the whitelist configuration from the configuration file;
/**
* Sa-Token相关配置
*/
@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
/**
* 注册sa-token拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
// 获取配置文件中的白名单路径
List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
// 登录认证:除白名单路径外均需要登录认证
SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
})).addPathPatterns("/**");
}
}
application.yml
whitelist configuration in the 06125c05a6e8de file is as follows, pay attention to the open access path and static resource path of Swagger;
# 访问白名单路径
secure:
ignored:
urls:
- /
- /swagger-ui/
- /*.html
- /favicon.ico
- /**/*.html
- /**/*.css
- /**/*.js
- /swagger-resources/**
- /v2/api-docs/**
- /actuator/**
- /admin/login
- /admin/isLogin
NotLoginException
exception when accessing the interface when not logged in, we need to deal with it globally;
/**
* 全局异常处理
* 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());
}
}
- Later, when we access the interface in the logged-in state, we can get the data;
- When we are not logged in (without token), we cannot access the interface normally, and the return
code
is401
.
Role authentication
Role authentication means that we define a set of rules. For example, theROLE-ADMIN
can access all resources under/brand
ROLE_USER
can only access/brand/listAll
. Next, let's implement role authentication.
- First, we need to extend the
StpInterface
interface of Sa-Token, and return the user's role code and permission code through the implementation method;
/**
* 自定义权限验证接口扩展
*/
@Component
public class StpInterfaceImpl implements StpInterface {
@Autowired
private UmsAdminService adminService;
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
AdminUser adminUser = adminService.getAdminById(Convert.toLong(loginId));
return adminUser.getRole().getPermissionList();
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
AdminUser adminUser = adminService.getAdminById(Convert.toLong(loginId));
return Collections.singletonList(adminUser.getRole().getName());
}
}
- Then configure routing rules in the Sa-Token interceptor, the
ROLE_ADMIN
can access all paths, whileROLE_USER
can only access the/brand/listAll
path;
/**
* Sa-Token相关配置
*/
@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
/**
* 注册sa-token拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
// 获取配置文件中的白名单路径
List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
// 登录认证:除白名单路径外均需要登录认证
SaRouter.match(Collections.singletonList("/**"), ignoreUrls, StpUtil::checkLogin);
// 角色认证:ROLE_ADMIN可以访问所有接口,ROLE_USER只能访问查询全部接口
SaRouter.match("/brand/listAll", () -> {
StpUtil.checkRoleOr("ROLE_ADMIN","ROLE_USER");
//强制退出匹配链
SaRouter.stop();
});
SaRouter.match("/brand/**", () -> StpUtil.checkRole("ROLE_ADMIN"));
})).addPathPatterns("/**");
}
}
- When the user is not allowed to access the role, Sa-Token will throw a
NotRoleException
exception, we can deal with it globally;
/**
* 全局异常处理
* Created by macro on 2020/2/27.
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理没有角色的异常
*/
@ResponseBody
@ExceptionHandler(value = NotRoleException.class)
public CommonResult handleNotRoleException(NotRoleException e) {
return CommonResult.forbidden(e.getMessage());
}
}
- We now have two users, the
admin
user has theROLE_ADMIN
role, and themacro
user has theROLE_USER
role;
- Use the
admin
account to access the/brand/list
interface, which can be accessed normally;
- Using the
macro
account to access the/brand/list
interface cannot be accessed normally, the returncode
is403
.
Authority authentication
When we assign permissions to roles, and then assign roles to users, users have these permissions. We can assign different permissions to each interface, and users with that permission can access the interface. This is permission authentication, and then we will implement it.
- We can configure routing rules in the Sa-Token interceptor. The
admin
user can access all paths, while themacro
user only has the permission to read, without the permission to write, modify, or delete;
/**
* Sa-Token相关配置
*/
@Configuration
public class SaTokenConfig implements WebMvcConfigurer {
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
/**
* 注册sa-token拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SaRouteInterceptor((req, resp, handler) -> {
// 获取配置文件中的白名单路径
List<String> ignoreUrls = 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("/**");
}
}
- When the user does not have permission to access, Sa-Token will throw a
NotPermissionException
exception, we can deal with it globally;
/**
* 全局异常处理
* Created by macro on 2020/2/27.
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理没有权限的异常
*/
@ResponseBody
@ExceptionHandler(value = NotPermissionException.class)
public CommonResult handleNotPermissionException(NotPermissionException e) {
return CommonResult.forbidden(e.getMessage());
}
}
- Use the
admin
account to access the/brand/delete
interface, which can be accessed normally;
- Using the
macro
account to access/brand/delete
cannot be accessed normally, and the returncode
is403
.
Summarize
Through a wave of practice on Sa-Token, we can find that its API design is very elegant, and it is indeed much smoother than Shiro and Spring Security. Sa-Token not only provides a series of powerful permissions-related functions, but also provides many standard solutions, such as Oauth2, distributed session sessions, etc. If you are interested, you can study it.
Reference
The official document of Sa-Token is very complete and conscientious. It not only provides solutions, but also provides solutions. It is strongly recommended that you check it out.
Official document: http://sa-token.dev33.cn/
Project source code address
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-sa-token
This article GitHub https://github.com/macrozheng/mall-learning has been included, welcome to Star!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。