
# 用户自定义认证使用
#### 工作原理
- 引入devspore-auth-starter依赖后，会在SDK内自动注册认证Filter：AuthAuthenticationFilter。
- 调用业务接口时会自动执行已注册的认证Filter，AuthAuthenticationFilter.doFilterInternal中将通过SPI方式先调用TokenParser的实现类（DefaultTokenParser，可自定义）完成token解析。
- AuthAuthenticationFilter.doFilterInternal在token解析成功后将解析出的用户信息通过SPI方式调用UserStore的实现类（DefaultUserStore，用户可自定义）存储用户信息供后续业务代码使用。
 
#### 约束与限制
TokenParser实现类与UserStore实现类，均需注册成bean，并完成bean扫描，确保Spring容器中可获取到对应bean。
 #### TokenParser接口介绍
```
package com.huaweicloud.devspore.auth.commons.spi;
import com.huawei.devspore.plugin.spi.authentication.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public interface TokenParser {
    /**
     * 根据token解析用户信息
     *
     * @param token
     * @param response
     * @return 用户信息 UserInfo
     */
    UserInfo parserToken(String token, HttpServletResponse response);
    /**
     * 根据HttpRequest解析用户信息
     * 
     * @param httpServletRequest
     * @param response
     * @return 用户信息 UserInfo
     */
    UserInfo parserToken(HttpServletRequest httpServletRequest, HttpServletResponse response);
}
```
两个方法，用户选其一实现即可；AuthAuthenticationFilter中通过devspore.auth.in-header配置值（默认true，配置说明详见下文"[配置说明]"章节）自动调用相应方法。
- 若token定义在header中，需实现parserToken（String token, HttpServletResponse response）接口，token获取根据配置由devspore-auth-starter获取。
- 若token未定义在header中，devspore.auth.in-header配置为false，并实现parserToken（HttpServletRequest httpServletRequest, HttpServletResponse response）接口。
 
 #### UserInfo用户信息类
```
package com.huawei.devspore.plugin.spi.authentication;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
 * 用户信息
 */
@Data
public class UserInfo {
    /**
     * 用户Id
     */
    private String userId;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 租户Id
     */
    private String tenantId;
    /**
     * 租户名
     */
    private String tenantName;
    /**
     * 请求token
     */
    private String token;
    /**
     * 用户角色
     */
    private List<String> roles;
    /**
     * JWTToken信息，仅JWTToken使用
     */
    private JwtInfo jwtInfo;
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Data
    public static final class JwtInfo implements Serializable {
        private static final long serialVersionUID = -2464405170204203810L;
        /**
         * 签发者
         */
        private String iss;
        /**
         * 主题
         */
        private String sub;
        /**
         * 签收者
         */
        private List<String> aud;
        /**
         * 过期时间
         */
        private String exp;
        /**
         * 生效时间
         */
        private String nbf;
        /**
         * 签发时间
         */
        private String iat;
        /**
         * Jwt唯一ID
         */
        private String jti;
    }
}
```
 #### 实现TokenParser接口示例
```
import com.huawei.devspore.plugin.spi.authentication.UserInfo;
import com.huaweicloud.devspore.auth.commons.spi.TokenParser;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
public class CustomTokenParser implements TokenParser {
    /**
     * 若token定义在header中，可使用该接口，token获取根据配置由devspore-auth获取
     *
     * @param token
     * @param response
     * @return UserInfo
     */
    @Override
    public UserInfo parserToken(String token, HttpServletResponse response) {
        // 自定义token解析
        ......
        // 判断token是否过期
        ......
        UserInfo userInfo = new UserInfo();
        // 根据解析后的token信息填充UserInfo对象
        ......
        return userInfo;
    }
    /**
     * 若token未定义在header中，使用该接口
     *
     * @param request
     * @param response
     * @return UserInfo
     */
    @Override
    public UserInfo parserToken(HttpServletRequest request, HttpServletResponse response) {
        // 从httpServletRequest获取token
        ......
        // 自定义token解析
        ......
        // 判断token是否过期
        ......
        UserInfo userInfo = new UserInfo();
        // 根据解析后的token信息填充UserInfo对象
        ......
        return userInfo;
    }
}
```
- token解析成功返回用户信息UserInfo，UserInfo中userId为空则默认解析失败。
- token解析失败，返回UserInfo为空，可抛出com.huaweicloud.devspore.auth.commons.exception.DevsporeAuthException，也可使用用户自定义异常。
 #### **UserStore接口介绍**
```
package com.huawei.devspore.plugin.spi.authentication;
/**
 * 用户信息接口
 */
public interface UserStore {
    /**
     * 设置用户信息
     * @param userInfo
     */
    void setUserInfo(UserInfo userInfo);
    /**
     * 获取用户信息
     * @return UserInfo
     */
    UserInfo getUserInfo();
}
```
- AuthAuthenticationFilter.doFilterInternal会在token解析成功后调用setUserInfo（UserInfo userInfo）接口存储用户信息。
- 后续业务接口鉴权或业务逻辑需要时调用getUserInfo()接口获取当前用户信息。
 
 #### 实现UserStore接口
```
import com.huawei.devspore.plugin.spi.authentication.UserInfo;
import com.huawei.devspore.plugin.spi.authentication.UserStore;
import org.springframework.stereotype.Component;
@Component
public class CustomUserStore implements UserStore {
    @Override
    public void setUserInfo(UserInfo userInfo) {
        // 自定义存储用户信息
        ......
    }
    @Override
    public UserInfo getUserInfo() {
        UserInfo userInfo = new UserInfo();
        // 自定义用户信息获取
        ......
        return userInfo;
    }
}
```
 #### 认证配置
一般服务配置devspore.auth.exclude-paths与devspore.auth.auth-patterns其中之一即可。
在配置文件中加入devspore相关配置，如下：
示例1：/healthz;/error;/swagger-ui.html;/swagger-ui/\*\*;/oneaccess-docs/\*\*;/api-docs/\*\*;等API无需认证，除此之外所有API均进行认证。
```
devspore:
  auth:
    exclude-paths: /healthz;/error;/swagger-ui.html;/swagger-ui/**;/oneaccess-docs/**;/api-docs/** # 不需要认证的路径，多个规则用;分隔
    in-header: true  # token是否在header中，默认true
    token-name: X-Auth-Token  # token在header中的参数名，inHeader设置为true时生效，默认X-Auth-Token
```
示例2：以"/v1/"开头的所有API均进行认证。
```
devspore:
  auth:
    auth-patterns: /v1/**  # 需要认证的路径，多个规则用;分隔
    in-header: true  # token是否在header中，默认true
    token-name: X-Auth-Token  # token在header中的参数名，inHeader设置为true时生效，默认X-Auth-Token
```
 #### 配置说明
表1配置说明 
| 参数名                         | 是否必须  | 功能                                                     |
|:---|:---|:---|
| devspore.auth.exclude-paths | false | 不需要认证的路径，多个规则用";"分隔，优先级大于devspore.auth.auth-patterns。  |
| devspore.auth.auth-patterns | false | 需要认证的路径，多个规则用";"分隔。                                    |
| devspore.auth.in-header     | false | token是否在header中，默认true。                                |
| devspore.auth.token-name    | false | token在header中的参数名，inHeader设置为true时生效，默认"X-Auth-Token"。 |
   
