The last chapter talked about the user identity authentication under micro services “SpringCloud Gateway identity Authentication”, this time mainly talked about how to carry out authentication. The identity authentication code in the previous chapter is slightly changed
Common Security frameworks in Java mainly include Spring Security and Shiro, both of which can provide very powerful functions but cost a lot to learn. Authentication under microservices will be more or less invasive to the service. In order to reduce dependency, reduce intrusion, and make the authentication function more transparent than the application service, we use the gateway to intercept resource requests for authentication.
1. Overall structure
The user authentication module resides in the API GateWay service, through which all API resource requests pass.
- doThe identity authentication, the user permission data is cached, but not returned
401
- doThe user authenticationAnd compares whether the currently accessed resource (URI and Method) is in the cached user permission data. If yes, the request is forwarded to the corresponding application service. If no, the request is returned
403
Two, implementation steps
1. Log in
public LoginUser login(String userName, String password){
// Check the password
User user = userService.checkUser(userName, password);
LoginUser loginUser = LoginUser.builder()
.userName(userName)
.realName(user.getRealName())
.userToken(UUID.randomUUID().toString())
.loginTime(new Date())
.build();
/ / save the session
session.saveSession(loginUser);
// Query permission
List<Permission> permissions = permissionRepository.findByUserName(userName);
// Save user permissions to cache
session.saveUserPermissions(userName, permissions);
return loginUser;
}
// ...
// Cache user permissions to Redis
public void saveUserPermissions(String userName, List<Permission> permissions) {
String key = String.format("login:permission:%s", userName);
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
hashOperations.putAll(key, permissions.stream().collect(
Collectors.toMap(p -> p.getMethod().concat(":").concat(p.getUri()),
Permission::getName, (k1, k2) -> k2)));
if(expireTime ! =null) { redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); }}Copy the code
- After the user is authenticated, this parameter is delivered
userToken
To save the current login information and cache the user authorization list - When caching authorization lists, do not directly save array objects as an object to save them as a hash list
2. Intercept the request
@Slf4j
@Component
public class AuthorizationFilter extends AbstractGatewayFilterFactory {
@Autowired
private Session session;
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String uri = request.getURI().getPath();
String method = request.getMethodValue();
Obtain userName from AuthenticationFilter
String key = "X-User-Name";
if(! request.getHeaders().containsKey(key)) { response.setStatusCode(HttpStatus.FORBIDDEN);return response.setComplete();
}
String userName = Objects.requireNonNull(request.getHeaders().get(key)).get(0);
// 2. Verify permissions
if(! session.checkPermissions(userName, uri, method)) { log.info("User: {}, no permissions", userName);
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
returnchain.filter(exchange); }; }}Copy the code
- The first step is to take it out
The identity authentication
module-deliveredX-User-Name - The second step is to check whether there are corresponding permissions in the cache
public boolean checkPermissions(String userName, String uri, String method) {
String key = String.format("login:permission:%s", userName);
String hashKey = String.format("%s:%s", method, uri);
if (redisTemplate.opsForHash().hasKey(key, hashKey)){
return true;
}
String allKey = "login:permission:all";
// If there is no permission in the list, pass
return! redisTemplate.opsForHash().hasKey(allKey, hashKey); }Copy the code
If not in the permission list, pass
It is mainly to pass some common resources that do not need to be configured and can be accessed by defaultlogin:permission:all
The list of all configured permissions needs to be cached when the program starts and the data needs to be kept up to date
3. Configure authentication Filter
spring:
cloud:
gateway:
routes:
- id: cloud-user
uri: lb://cloud-user # backend service name
predicates:
- Path=/user/** # route address
filters:
- name: AuthenticationFilter # Identity authentication
- name: AuthorizationFilter User authentication
- StripPrefix=1 # remove prefix
Copy the code
- Pay special attention to the filter sequence. You must perform identity authentication before authentication
- If multiple routes need to be configured, use this command
default-filters
Default Filter Configuration
Other issues
The following error occurs during unit testing
nested exception is java.lang.NoClassDefFoundError: javax/validation/ValidationException
Copy the code
Please upgrade the dependency package version:
<! Update validation-api version -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.5/6.5.4. The Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1. The Final</version>
</dependency>
Copy the code
4. Complete code
Gitee.com/hypier/barr…