Spring Cloud Gateway + Oauth2 to implement unified authentication and authentication! In fact, AT the beginning of the integration, I was not able to play, and I looked up information and source code, and finally succeeded. Recently I tried the micro-service permission solution provided by SA-Token. It feels very elegant to use. I recommend it to you!
SpringBoot e-commerce project mall (50K + STAR) address: github.com/macrozheng/…
Front knowledge
We will use Nacos as the registry, Gateway as the Gateway, and sa-Token as the micro-service permission solution, which is based on the previous solution. For those who are not familiar with these technologies, please read the following article.
- Spring Cloud Gateway: a new generation of API Gateway services
- Spring Cloud Alibaba: Nacos is used as a registry and configuration center
- Spring Cloud Gateway + Oauth2 to achieve unified authentication and authentication!
- Sa-token Usage Tutorial
Application architecture
Authentication service is responsible for login processing, gateway is responsible for login authentication and permission authentication, and other API services are responsible for handling their own business logic. To enable sa-Token sessions to be shared among multiple services, all services need to integrate SA-Token and Redis.
- Micro-sa-token-common: a user class common to other services
UserDTO
And generic return result classesCommonResult
They’re drawn here. - Micro-sa-token-gateway: indicates the gateway service, which is responsible for request forwarding, login authentication, and permission authentication.
- Micro-sa-token-auth: an authentication service that contains only one login interface and invokes the API implementation of sa-Token.
- Micro-sa-token-api: indicates the protected API service. Users can access the service after passing the network authentication.
Plan implementation
The next step is to implement this solution and set up gateway service, authentication service and API service in turn.
micro-sa-token-gateway
We will first set up the gateway service, which will be responsible for the login authentication and permission authentication of the entire micro-service.
- In addition to the generic Gateway dependencies, we also need to be in
pom.xml
To add the following dependencies, including the SA-Token Reactor responsive dependency, the Redis integrated distributed Session dependency and ourmicro-sa-token-common
Rely on;
<dependencies>
<! Sa-token Authorization (Reactor Responsive Integration) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<! -- Sa-token integration Redis (using Jackson serialization) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<! Redis connection pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<! -- Micro-sa-Token common dependencies -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Copy the code
- Next modify the configuration file
application.yml
Add Redis configuration and sa-token configuration if you read the previous articleSa-token Usage TutorialThen you basically know what these configurations do;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# Sa - Token configuration
sa-token:
# token name (also cookie name)
token-name: Authorization
# Token validity period, in seconds. -1 indicates that the token will never expire
timeout: 2592000
# Token temporary validity period (if no operation is performed within the specified period, the token is regarded as expired), in seconds
activity-timeout: - 1
# allow concurrent logins with the same account (false)
is-concurrent: true
# Whether to share one token when multiple users log in to the same account (if false, create one token for each login)
is-share: false
Style # token
token-style: uuid
# Whether to output operation logs
is-log: false
Whether to read token from cookie
is-read-cookie: false
Whether to read the token from head
is-read-head: true
Copy the code
- Add an sa-Token configuration class
SaTokenConfig
Inject a filter for login authentication and permission authentication insetAuth
Method to add routing rules insetError
Method to add callback processing for authentication failure.
@Configuration
public class SaTokenConfig {
/** * Register the sa-token global filter */
@Bean
public SaReactorFilter getSaReactorFilter(a) {
return new SaReactorFilter()
// Intercept the address
.addInclude("/ * *")
// Open the address
.addExclude("/favicon.ico")
// Authentication method: access each time
.setAuth(r -> {
// Login authentication: authentication is required except for the login interface
SaRouter.match("/ * *"."/auth/user/login", StpUtil::checkLogin);
// Permission authentication: Different interfaces have different access permissions
SaRouter.match("/api/test/hello", () -> StpUtil.checkPermission("api:test:hello"));
SaRouter.match("/api/user/info", () -> StpUtil.checkPermission("api:user:info"));
})
// setAuth method exception processing
.setError(e -> {
// Set the error return format to JSON
ServerWebExchange exchange = SaReactorSyncHolder.getContent();
exchange.getResponse().getHeaders().set("Content-Type"."application/json; charset=utf-8");
returnSaResult.error(e.getMessage()); }); }}Copy the code
- Extension provided under sa-Token
StpInterface
The interface is used to obtain the permission of the user. After the user logs in, the user information will be stored in the Session, and the permission information will also be stored in the Session, so the permission code can only be obtained from the Session.
/** * Custom permission validation interface extension */
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// Returns the list of permission codes owned by this loginId
UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
return userDTO.getPermissionList();
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// Returns the list of role codes owned by this loginId
return null; }}Copy the code
micro-sa-token-auth
Next, we will set up the authentication service, as long as the integration of SA-Token and the implementation of the login interface, very simple.
- First of all in
pom.xml
To add related dependencies, including sa-token SpringBoot dependency, integration Redis to implement distributed Session dependency and ourmicro-sa-token-common
Rely on;
<dependencies>
<! -- Sa-token authentication -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<! -- Sa-token integration Redis (using Jackson serialization) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<! Redis connection pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<! -- Micro-sa-Token common dependencies -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Copy the code
- Next modify the configuration file
application.yml
, just copy the previous gateway configuration;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# Sa - Token configuration
sa-token:
# token name (also cookie name)
token-name: Authorization
# Token validity period, in seconds. -1 indicates that the token will never expire
timeout: 2592000
# Token temporary validity period (if no operation is performed within the specified period, the token is regarded as expired), in seconds
activity-timeout: - 1
# allow concurrent logins with the same account (false)
is-concurrent: true
# Whether to share one token when multiple users log in to the same account (if false, create one token for each login)
is-share: false
Style # token
token-style: uuid
# Whether to output operation logs
is-log: false
Whether to read token from cookie
is-read-cookie: false
Whether to read the token from head
is-read-head: true
Copy the code
- in
UserController
The login interface is defined in. The Token is returned after successful loginUserServiceImpl
In the class;
/** * Created by macro on 2020/7/17. */
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceImpl userService;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public CommonResult login(@RequestParam String username, @RequestParam String password) {
SaTokenInfo saTokenInfo = userService.login(username, password);
if (saTokenInfo == null) {
return CommonResult.validateFailed("Wrong username or password");
}
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("token", saTokenInfo.getTokenValue());
tokenMap.put("tokenHead", saTokenInfo.getTokenName());
returnCommonResult.success(tokenMap); }}Copy the code
- in
UserServiceImpl
To add the logics for login, first verify the password, then notify the user ID of the sa-Token login, and then store the user information directly into the Session.
/** * Created by macro on 2020/6/19. */
@Service
public class UserServiceImpl{
private List<UserDTO> userList;
public SaTokenInfo login(String username, String password) {
SaTokenInfo saTokenInfo = null;
UserDTO userDTO = loadUserByUsername(username);
if (userDTO == null) {
return null;
}
if(! SaSecureUtil.md5(password).equals(userDTO.getPassword())) {return null;
}
// After the password verification is successful, you can log in with one line of code
StpUtil.login(userDTO.getId());
// Store user information in Session
StpUtil.getSession().set("userInfo",userDTO);
// Obtain the Token information of the current login user
saTokenInfo = StpUtil.getTokenInfo();
returnsaTokenInfo; }}Copy the code
- It is important to note that sa-Token’s Session is not HttpSession, but its own session-like mechanism.
micro-sa-token-api
Next, let’s build a protected API service to implement the interface for obtaining login user information and the test interface that requires special permissions to access.
- First of all in
pom.xml
To add dependencies, and abovemicro-sa-token-auth
The same;
<dependencies>
<! -- Sa-token authentication -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.24.0</version>
</dependency>
<! -- Sa-token integration Redis (using Jackson serialization) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.24.0</version>
</dependency>
<! Redis connection pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<! -- Micro-sa-Token common dependencies -->
<dependency>
<groupId>com.macro.cloud</groupId>
<artifactId>micro-sa-token-common</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Copy the code
- Next modify the configuration file
application.yml
, just copy the previous gateway configuration;
spring:
redis:
database: 0
port: 6379
host: localhost
password:
# Sa - Token configuration
sa-token:
# token name (also cookie name)
token-name: Authorization
# Token validity period, in seconds. -1 indicates that the token will never expire
timeout: 2592000
# Token temporary validity period (if no operation is performed within the specified period, the token is regarded as expired), in seconds
activity-timeout: - 1
# allow concurrent logins with the same account (false)
is-concurrent: true
# Whether to share one token when multiple users log in to the same account (if false, create one token for each login)
is-share: false
Style # token
token-style: uuid
# Whether to output operation logs
is-log: false
Whether to read token from cookie
is-read-cookie: false
Whether to read the token from head
is-read-head: true
Copy the code
- Add the interface to obtain user information, because the use of Redis to achieve distributed Session, directly from the Session can obtain, is not very simple!
** Created by macro on 2020/6/19. */
@RestController
@RequestMapping("/user")
public class UserController{
@GetMapping("/info")
public CommonResult<UserDTO> userInfo(a) {
UserDTO userDTO = (UserDTO) StpUtil.getSession().get("userInfo");
returnCommonResult.success(userDTO); }}Copy the code
- Add the need
api:test:hello
Permissions to access the test interface, presetadmin
The user has the permission, andmacro
There are no users.
/** * Created by macro on 2020/6/19. */
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
public CommonResult hello(a) {
return CommonResult.success("Hello World."); }}Copy the code
Function demonstration
After the establishment of the three services, we use Postman to demonstrate the authentication and authorization functions of micro-services.
- Start Nacos and Redis services first, and then start again
micro-sa-token-gateway
,micro-sa-token-auth
andmicro-sa-token-api
Services, startup order does not matter;
- Directly through the gateway access login interface to get Token, access to the address: http://localhost:9201/auth/user/login
- Access API services through the gateway,
Without a Token
The interface for obtaining user information is called, but cannot be accessed normally. The access address is:http://localhost:9201/api/user/info
- Access API services through the gateway,
With a Token
The interface for obtaining user information can be accessed normally.
- Access the API service through the gateway
macro
User access requirementsapi:test:hello
Permission test interface, cannot access, access address:http://localhost:9201/api/test/hello
- Login switch to
admin
User, the user hasapi:test:hello
Permissions;
- Access the API service through the gateway
admin
The user can access the test interface normally.
conclusion
The Sa-Token solution is simpler and more elegant than previous microservice permission solutions using Spring Security. To use Security, we need to define the authentication manager, handle the unauthenticated and unauthorized cases separately, and define the authentication and resource server configuration ourselves, which is very tedious to use. Using sa-Token, you only need to configure a filter on the gateway to implement authentication and authorization, and then invoke the API to implement login and permission assignment. The specific difference can refer to the figure below.
The resources
Official document: sa-token.dev33.cn/
Project source code address
Github.com/macrozheng/…