认证管理器会将真正的认证任务委派给认证提供者,您也可以自定义认证提供者,只需要实现接口 AuthenticationProvider,并以 AuthenticationProvider 接口为 id 注入到 IoC 容器即可。
定义
export interface AuthenticationProvider {
readonly priority: number;
authenticate(): Promise<Authentication>;
support(): Promise<boolean>;
}
support 方法往往是匹配当前请求的路由是否为我们指定的即可。示例如下:
async support(): Promise<boolean> {
return !!await this.requestMatcher.match(this.options.loginUrl, this.options.loginMethod);
}
默认实现
@Component(AuthenticationProvider)
export class AuthenticationProviderImpl implements AuthenticationProvider {
@Value('malagu.security')
protected readonly options: any;
@Autowired(PasswordEncoder)
protected readonly passwordEncoder: PasswordEncoder;
@Autowired(UserStore)
protected readonly userStore: UserStore;
@Autowired(UserChecker)
protected readonly userChecker: UserChecker;
@Autowired(RequestMatcher)
protected readonly requestMatcher: RequestMatcher;
priority = DEFAULT_AUTHENTICATION_PROVIDER__PRIORITY;
async authenticate(): Promise<Authentication> {
const username = this.doGetValue(this.options.usernameKey);
const password = this.doGetValue(this.options.passwordKey);
if (!password || !username) {
throw new BadCredentialsError('Bad credentials');
}
const user = await this.userStore.load(username);
await this.userChecker.check(user);
if (!await this.passwordEncoder.matches(password, user.password)) {
throw new BadCredentialsError('Bad credentials');
}
Context.getResponse().statusCode = 302;
Context.getResponse().setHeader('Location', this.options.loginSuccessUrl);
return {
principal: user,
credentials: '',
policies: user.policies,
authenticated: true
};
}
protected doGetValue(key: string): string {
const request = Context.getRequest();
if (request.body) {
return request.body[key];
} else {
return request.query[key];
}
}
async support(): Promise<boolean> {
return !!await this.requestMatcher.match(this.options.loginUrl, this.options.loginMethod);
}
}