
## 安装

```bash
npm install unified-login-sdk --save
# 或
yarn add unified-login-sdk
```

## 快速开始

### 基本使用

```typescript
import unifiedLoginSDK from 'unified-login-sdk';

// 初始化配置
unifiedLoginSDK.init({
  clientId: 'your-client-id',
  authorizationEndpoint: 'https://auth.example.com/authorize',
  tokenEndpoint: 'https://auth.example.com/token',
  userInfoEndpoint: 'https://auth.example.com/userinfo',
  redirectUri: 'https://your-app.example.com/callback',
  storageType: 'localStorage',
  autoRefreshToken: true,
  tenantId: 'your-tenant-id' // 可选，会自动添加到请求头中的tenant-id字段
});

// 登录
document.getElementById('login-btn')?.addEventListener('click', () => {
  unifiedLoginSDK.login();
});

// 处理回调
if (unifiedLoginSDK.isAuthenticated()) {
  // 已登录，获取用户信息
  unifiedLoginSDK.getUserInfo().then(userInfo => {
    console.log('User info:', userInfo);
  });
} else if (unifiedLoginSDK.isCallback()) {
  // 处理授权回调
  unifiedLoginSDK.handleCallback().then(userInfo => {
    console.log('Login successful:', userInfo);
    // 跳转到首页
    window.location.href = '/';
  }).catch(error => {
    console.error('Login failed:', error);
  });
}

// 退出登录
document.getElementById('logout-btn')?.addEventListener('click', () => {
  unifiedLoginSDK.logout().then(() => {
    console.log('Logout successful');
    window.location.href = '/login';
  });
});
```

## 核心功能

### 初始化配置

```typescript
unifiedLoginSDK.init({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret', // 可选，某些场景下需要
  authorizationEndpoint: 'https://auth.example.com/authorize',
  tokenEndpoint: 'https://auth.example.com/token',
  userInfoEndpoint: 'https://auth.example.com/userinfo',
  redirectUri: 'https://your-app.example.com/callback',
  storageType: 'localStorage', // 可选，默认localStorage
  autoRefreshToken: true, // 可选，默认true
  permissionsEndpoint: 'https://auth.example.com/permissions' // 可选，权限端点
});
```

### 登录流程

1. 调用`login()`方法跳转到授权页面
2. 用户在授权页面登录并授权
3. 授权服务器重定向到配置的`redirectUri`
4. 调用`handleCallback()`方法处理授权回调，获取用户信息

### Token管理

```typescript
// 获取访问令牌
const accessToken = unifiedLoginSDK.getAccessToken();

// 刷新令牌
unifiedLoginSDK.refreshToken().then(() => {
  console.log('Token refreshed');
}).catch(error => {
  console.error('Failed to refresh token:', error);
});

// 检查是否已认证
const isAuthenticated = unifiedLoginSDK.isAuthenticated();
```

### 用户信息管理

```typescript
// 获取用户信息
unifiedLoginSDK.getUserInfo().then(userInfo => {
  console.log('User info:', userInfo);
});

// 获取用户权限列表
unifiedLoginSDK.getPermissions().then(permissions => {
  console.log('Permissions:', permissions);
});
```

### 事件监听

```typescript
// 监听登录事件
unifiedLoginSDK.on('login', () => {
  console.log('User logged in');
});

// 监听退出事件
unifiedLoginSDK.on('logout', () => {
  console.log('User logged out');
});

// 监听Token过期事件
unifiedLoginSDK.on('tokenExpired', () => {
  console.log('Token expired');
  // 可以在这里执行自定义逻辑，如跳转到登录页
  unifiedLoginSDK.login();
});

// 移除事件监听
const handleLogin = () => console.log('User logged in');
unifiedLoginSDK.on('login', handleLogin);
unifiedLoginSDK.off('login', handleLogin);
```

## 框架集成

### Vue 2

```javascript
// main.js
import Vue from 'vue';
import { createVuePlugin } from 'unified-login-sdk';
import App from './App.vue';
import router from './router';

// 创建Vue插件
const vuePlugin = createVuePlugin('localStorage');

// 安装插件
Vue.use(vuePlugin, {
  config: {
    clientId: 'your-client-id',
    authorizationEndpoint: 'https://auth.example.com/authorize',
    tokenEndpoint: 'https://auth.example.com/token',
    userInfoEndpoint: 'https://auth.example.com/userinfo',
    redirectUri: 'https://your-app.example.com/callback'
  }
});

new Vue({
  router,
  render: h => h(App)
}).$mount('#app');
```

在组件中使用：

```vue
<template>
  <div>
    <div v-if="$auth.isAuthenticated()">
      <h1>Welcome, {{ userInfo?.name }}</h1>
      <button @click="logout">Logout</button>
    </div>
    <div v-else>
      <button @click="login">Login</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userInfo: null
    };
  },
  mounted() {
    if (this.$auth.isAuthenticated()) {
      this.getUserInfo();
    } else if (this.$auth.isCallback()) {
      this.handleCallback();
    }
  },
  methods: {
    login() {
      this.$auth.login();
    },
    async logout() {
      await this.$auth.logout();
      window.location.href = '/';
    },
    async getUserInfo() {
      this.userInfo = await this.$auth.getUserInfo();
    },
    async handleCallback() {
      try {
        this.userInfo = await this.$auth.handleCallback();
        window.location.href = '/';
      } catch (error) {
        console.error('Login failed:', error);
      }
    }
  }
};
</script>
```

### Vue 3

```javascript
// main.js
import { createApp } from 'vue';
import { createVuePlugin } from 'unified-login-sdk';
import App from './App.vue';
import router from './router';

// 创建Vue插件
const vuePlugin = createVuePlugin('localStorage');

const app = createApp(App);

// 安装插件
app.use(vuePlugin, {
  config: {
    clientId: 'your-client-id',
    authorizationEndpoint: 'https://auth.example.com/authorize',
    tokenEndpoint: 'https://auth.example.com/token',
    userInfoEndpoint: 'https://auth.example.com/userinfo',
    redirectUri: 'https://your-app.example.com/callback'
  }
});

app.use(router);
app.mount('#app');
```

在组件中使用（Composition API）：

```vue
<template>
  <div>
    <div v-if="isAuthenticated">
      <h1>Welcome, {{ userInfo?.name }}</h1>
      <button @click="logout">Logout</button>
    </div>
    <div v-else>
      <button @click="login">Login</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, inject } from 'vue';

// 注入SDK实例
const auth = inject('unifiedLogin');
const userInfo = ref(null);
const isAuthenticated = ref(auth.isAuthenticated());

onMounted(() => {
  if (isAuthenticated.value) {
    getUserInfo();
  } else if (auth.isCallback()) {
    handleCallback();
  }
});

const login = () => {
  auth.login();
};

const logout = async () => {
  await auth.logout();
  window.location.href = '/';
};

const getUserInfo = async () => {
  userInfo.value = await auth.getUserInfo();
};

const handleCallback = async () => {
  try {
    userInfo.value = await auth.handleCallback();
    isAuthenticated.value = true;
    window.location.href = '/';
  } catch (error) {
    console.error('Login failed:', error);
  }
};
</script>
```
```

## API参考

### 初始化

```typescript
init(config: SDKConfig): void
```

初始化SDK配置。

### 登录

```typescript
login(redirectUri?: string): void
```

触发登录流程，可选参数`redirectUri`可覆盖初始化时的配置。

### 退出登录

```typescript
logout(): Promise<void>
```

退出登录，清除本地存储的Token和用户信息。

### 处理授权回调

```typescript
handleCallback(): Promise<UserInfo>
```

处理授权回调，获取用户信息。

### 获取用户信息

```typescript
getUserInfo(): Promise<UserInfo>
```

获取用户基本信息。

### 获取用户权限列表

```typescript
getPermissions(): Promise<string[]>
```

获取用户权限列表。

### 检查是否已认证

```typescript
isAuthenticated(): boolean
```

检查用户是否已认证。

### 获取访问令牌

```typescript
getAccessToken(): string | null
```

获取访问令牌。

### 刷新访问令牌

```typescript
refreshToken(): Promise<void>
```

刷新访问令牌。

### 事件监听

```typescript
on(event: 'login' | 'logout' | 'tokenExpired', callback: Function): void
```

监听登录、退出或Token过期事件。

### 移除事件监听

```typescript
off(event: 'login' | 'logout' | 'tokenExpired', callback: Function): void
```

移除事件监听。

## 配置选项

| 选项 | 类型 | 必填 | 默认值 | 描述 |
|------|------|------|--------|------|
| clientId | string | 是 | - | 客户端ID |
| clientSecret | string | 否 | - | 客户端密钥，某些场景下需要 |
| authorizationEndpoint | string | 是 | - | 授权端点URL |
| tokenEndpoint | string | 是 | - | Token端点URL |
| userInfoEndpoint | string | 是 | - | 用户信息端点URL |
| redirectUri | string | 是 | - | 重定向URL |
| storageType | 'localStorage'  'sessionStorage'  'cookie' | 否 | 'localStorage' | Token存储类型 |
| autoRefreshToken | boolean | 否 | true | 是否自动刷新Token |
| permissionsEndpoint | string | 否 | - | 权限端点URL |
| stateLength | number | 否 | 32 | 状态参数长度 |
| tenantId | string | 否 | - | 租户ID，会自动添加到请求头中的tenant-id字段 |

## 事件处理

| 事件 | 描述 |
|------|------|
| login | 用户登录成功时触发 |
| logout | 用户退出登录时触发 |
| tokenExpired | Token过期时触发 |

## 路由守卫

### Vue路由守卫

```javascript
// router/index.js
import VueRouter from 'vue-router';
import { Auth } from 'unified-login-sdk';
import { Storage } from 'unified-login-sdk';
import { RouterGuard } from 'unified-login-sdk';

const storage = new Storage('localStorage');
const auth = new Auth(storage);
const routerGuard = new RouterGuard(auth);

const router = new VueRouter({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/protected',
      name: 'Protected',
      component: Protected,
      meta: {
        auth: {
          requiresAuth: true,
          requiredPermissions: ['read:protected']
        }
      }
    }
  ]
});

// 添加路由守卫
router.beforeEach(routerGuard.createVueGuard());

export default router;
```



## 错误处理

### 网络错误处理

```typescript
try {
  await unifiedLoginSDK.getUserInfo();
} catch (error) {
  if (error.name === 'HttpError') {
    // 处理HTTP错误
    console.error('HTTP Error:', error.status, error.message);
    if (error.status === 401) {
      // 未授权，跳转到登录页
      unifiedLoginSDK.login();
    } else if (error.status === 403) {
      // 权限不足
      window.location.href = '/403';
    }
  } else {
    // 处理其他错误
    console.error('Error:', error.message);
  }
}
```

### Token失效处理

```typescript
// 监听Token过期事件
unifiedLoginSDK.on('tokenExpired', () => {
  console.log('Token expired');
  // 跳转到登录页
  unifiedLoginSDK.login();
});
```

## 最佳实践

1. **配置安全存储**：根据项目需求选择合适的存储类型，敏感信息建议使用cookie并设置secure和httpOnly标志。

2. **合理设置Token过期时间**：根据项目安全性要求设置合适的Token过期时间，建议access token过期时间较短，refresh token过期时间较长。

3. **使用路由守卫保护敏感路由**：对需要登录或特定权限的路由使用路由守卫进行保护。

4. **处理网络错误**：在调用SDK方法时，使用try-catch捕获并处理可能的错误。

5. **监听Token过期事件**：及时处理Token过期情况，避免用户体验下降。

6. **不要直接暴露clientSecret**：clientSecret应该只在后端使用，前端SDK尽量避免使用clientSecret。

7. **使用HTTPS**：确保所有与授权服务器的通信都使用HTTPS，避免Token被窃取。

8. **定期清理存储**：在用户退出登录时，确保清理所有相关存储的信息。

## 浏览器兼容性

- Chrome (推荐)
- Firefox
- Safari
- Edge

## 许可证

MIT License
