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.

  1. doThe identity authentication, the user permission data is cached, but not returned401
  2. 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 returned403

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 delivereduserTokenTo 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 outThe identity authenticationmodule-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, passIt is mainly to pass some common resources that do not need to be configured and can be accessed by default
  • login:permission:allThe 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 commanddefault-filtersDefault 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…

Five, please pay attention to my official number