外观
快速开始
本例在已有 Spring Boot Web 应用中注册一个 AuthenticationLoginUser,将两种演示凭证映射为读者和编辑者,再通过 SessionUserUtil 读取身份并检查操作权限。无需数据库、Store、Token 组件或 Business 模块。
准备 Java 25、Spring Boot 4.1.0,以及可以解析的 EFC 4.1.0-SNAPSHOT 构件。应用应已有启动类和编译/启动配置;构建基线见统一 Dependencies。使用一个尚未配置其他身份 Provider 的演示应用,避免多个实现竞争。
1. 添加依赖
在应用 POM 中导入 Component BOM 并加入 Authentication;已有统一 BOM 管理同版本时不重复导入。
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.own</groupId>
<artifactId>springboot-component-dependencies</artifactId>
<version>4.1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.own.component</groupId>
<artifactId>springboot-component-authentication</artifactId>
</dependency>
</dependencies>创建 application-authentication-demo.yml:
yaml
own:
auth:
cache-session-user: true
security:
login-verification: true此处显式开启取用户时的登录检查,因为 own.security.login-verification 默认是 false。请求缓存也默认关闭,示例开启后同一请求可复用已取得的身份;它不创建 HTTP Session 或 Redis 会话。
2. 实现演示身份来源
将下面配置类放入应用扫描范围,按实际包名调整。固定凭证仅用于 authentication-demo profile 下的接入验证,正式实现应替换为服务端可信的凭证验证和权限查询,不应按请求传入的 userId 直接构造已登录用户。
java
package com.example.demo;
import com.own.component.authentication.AuthenticationLoginUser;
import com.own.component.authentication.user.DefaultLoginUser;
import com.own.component.authentication.user.DefaultPermissionUser;
import com.own.component.authentication.util.UserUtil;
import com.own.component.base.login.user.BaseLoginUser;
import com.own.component.base.login.user.BasePermissionUser;
import com.own.constant.ConstantAccount;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Profile("authentication-demo")
@Configuration(proxyBeanMethods = false)
public class AuthenticationDemoConfiguration {
@Bean
AuthenticationLoginUser demoAuthenticationLoginUser(UserUtil userUtil) {
Map<String, BaseLoginUser> users = Map.of(
"demo-reader", new DefaultLoginUser("demo-reader", 42L,
"演示读者", ConstantAccount.UserTypeEnum.NORMAL, "web"),
"demo-editor", new DefaultLoginUser("demo-editor", 43L,
"演示编辑者", ConstantAccount.UserTypeEnum.NORMAL, "web"));
return new AuthenticationLoginUser() {
@Override
public BaseLoginUser loginUser() {
String token = userUtil.getAuthToken(UserUtil.Space.HEADER, "X-Demo-Token");
return token == null ? null : users.get(token);
}
@Override
public BasePermissionUser permissionUser(BaseLoginUser user) {
if (user == null || !user.isLogin()) {
return null;
}
boolean editor = Long.valueOf(43L).equals(user.userId());
Set<String> allowed = editor ? Set.of("read", "export") : Set.of("read");
return new DefaultPermissionUser(user,
List.of(editor ? "editor" : "reader"),
(module, operations) -> "demo-report".equals(module)
&& operations != null && operations.length > 0
&& Arrays.stream(operations).allMatch(allowed::contains));
}
@Override
public List<BaseLoginUser> loginUser(Long userId) {
return users.values().stream()
.filter(user -> user.userId().equals(userId)).toList();
}
@Override
public List<BasePermissionUser> permissionUser(Long userId) {
return loginUser(userId).stream().map(this::permissionUser).toList();
}
};
}
}未传凭证或凭证不匹配时返回 null,父适配器会回退为未登录用户;不会创建“匿名但已登录”的对象。按 userId 查询的两个重载也明确返回列表,避免使用接口默认的 null 返回值。
3. 添加验证接口
将以下 Controller 放入相同扫描范围。权限检查由方法显式执行,示例无需 Security 切面。
java
package com.example.demo;
import com.own.component.base.login.util.SessionUserUtil;
import com.own.component.base.model.R;
import com.own.component.base.model.ResultModel;
import com.own.constant.exception.BusinessSimpleException;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@Profile("authentication-demo")
@RestController
@RequestMapping("/authentication-demo")
public class AuthenticationDemoController {
@GetMapping("/me")
public ResultModel<IdentityView> me() {
var user = SessionUserUtil.getLoginUser();
var permission = SessionUserUtil.getPermissionUser();
return R.success(new IdentityView(user.userId(), user.userName(),
user.client(), permission.roleNameList()));
}
@GetMapping("/operation/{operation}")
public ResultModel<String> operation(@PathVariable("operation") String operation) {
var user = SessionUserUtil.getPermissionUser();
if (!user.checkOperation("demo-report", new String[]{operation})) {
throw new BusinessSimpleException("没有该演示操作的权限");
}
return R.success("操作权限检查通过");
}
public record IdentityView(Long userId, String userName,
String client, Collection<String> roles) {
}
}响应只投影需要的字段,不直接返回含 Token 的登录对象。operation 接口仅验证回调结果,不生成报表或导出文件。
4. 发起请求
按应用原有启动方式增加 --spring.profiles.active=authentication-demo,已有其他必要 profile 时一并保留。以下以本机 8080 端口为例:
sh
curl -sS 'http://127.0.0.1:8080/authentication-demo/me' \
-H 'X-Demo-Token: demo-reader'
curl -sS 'http://127.0.0.1:8080/authentication-demo/operation/read' \
-H 'X-Demo-Token: demo-reader'
curl -sS 'http://127.0.0.1:8080/authentication-demo/operation/export' \
-H 'X-Demo-Token: demo-reader'
curl -sS 'http://127.0.0.1:8080/authentication-demo/operation/export' \
-H 'X-Demo-Token: demo-editor'
curl -sS 'http://127.0.0.1:8080/authentication-demo/me'| 请求 | 预期结果 |
|---|---|
| 读者查询 me | code=00000,data 中 userId="42"、userName="演示读者"、client="web"、roles=["reader"] |
| 读者 read | 权限检查通过,code=00000 |
| 读者 export | 业务失败,code=E0001、success=false |
| 编辑者 export | 权限检查通过,code=00000 |
| 无凭证或错误凭证查询 me | 未登录,code=U0009、success=false |
HTTP 状态不等于业务结果,完整响应边界见 Base 响应约定。接入自己的身份来源后,继续核对请求缓存与配置;需要业务角色、失效时间戳和状态清理时阅读权限与状态扩展。