前言:
版本信息:
- springboot: 2.3.0.RELEASE
- Spring Cloud Hoxton: Hoxton.SR3
- Spring Cloud Alibaba: 2.2.1.RELEASE
一.涉及到的微服务模块以及技术有:
1.微服务
- 管理员资源服务
- app用户资源服务
- 网关
- 认证中心
- 公共资源服务
2.技术或组件
- spring gateway----网关
- alibaba nacos -----注册中心
- spring security ------认证授权
- jwt ---------------------生成token
- mybatis plus ---------持久层框架
- my sql -----------------数据存储
- Feign -----------------服务间的远程调用
二.实现的功能:
1.网关
统一系统接口入口,在网关进行鉴权操作,
(取出token中的权限信息进行鉴权)
默认负载均衡策略为:轮询策略
2.接口设计
2.1 三组资源:
- admin资源,
- 用户资源,
- 公共资源
(1).admin资源:
自然是只有admin角色才可访问
(2).用户资源:
分别有
- 用户访问资源
- admin访问资源(需要有admin角色可访问)
(3).公共资源
略…
3.认证中心
进行登录用户的认证授权,返回用户名和token的json数据,
权限信息放在token中
4.其他服务
顾名思义
5.接口url设计
注:
app接口需要拥有user角色的用户才可访问
admin接口需要拥有admin角色才可访问
app接口: /前缀/api-服务名/v1/api/**
admin接口: /前缀/api-服务名/v1/admin/**
5.项目服务
三.数据表设计
四:代码实现
1.项目父工程
(1).依赖
<properties>
<java.version>1.8</java.version>
</properties>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud Hoxton -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.网关
(1).依赖
<dependencies>
<!--项目公共模块-->
<dependency>
<groupId>com.qiongqi.taoyiquan</groupId>
<artifactId>taoyiquan-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</exclusion>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
(2).yml配置
server:
port: 8849
spring:
application:
name: taoyiquan-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: taoyiquan-admin
uri: lb://taoyiquan-admin # lb: 使用负载均衡策略 默认应该是轮询策略
predicates:
- Path=/admin/**
filters:
- StripPrefix=1
- id: taoyiquan-user
uri: lb://taoyiquan-user
predicates:
- Path=/user/**
filters:
- StripPrefix=1
- id: taoyiquan-auth
uri: lb://taoyiquan-auth
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
- id: taoyiquan-taobao
uri: lb://taoyiquan-taobao
predicates:
- Path=/taobao/**
filters:
- StripPrefix=1
(3).security配置
/**
* @description: security配置
* @author: ※狗尾巴草
* @date: 2020-11-26 12:01
**/
@EnableWebFluxSecurity
public class SecurityConfig {
@Autowired
private CustomizeAuthenticationEntryPoint authenticationEntryPoint;
//security的鉴权排除的url列表
private static final String[] excludedAuthPages = {
"/auth/api-auth/v1/login",//登录
};
@Bean
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http){
http
.authorizeExchange()
// 需要权限访问de接口
.pathMatchers("/admin/**","/user/api-user/*/admin/**").access(new XTReactiveAuthorizationManager("admin"))
.pathMatchers("/user/api-user/*/api/**").access(new XTReactiveAuthorizationManager("admin","user"))
//无需权限访问
// .pathMatchers(excludedAuthPages).permitAll() //无需进行权限过滤的请求路径
// .pathMatchers(HttpMethod.OPTIONS).permitAll() //option 请求默认放行
.anyExchange().permitAll()//无需权限访问
.and()
.httpBasic()
// .authenticationEntryPoint()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)// 匿名访问
.and() .csrf().disable()//必须支持跨域
.logout().disable()
;
SecurityWebFilterChain chain = http.build();
Iterator<WebFilter> weIterable = chain.getWebFilters().toIterable().iterator();
while(weIterable.hasNext()) {
WebFilter f = weIterable.next();
if(f instanceof AuthenticationWebFilter) {
AuthenticationWebFilter webFilter = (AuthenticationWebFilter) f;
//将自定义的AuthenticationConverter添加到过滤器中
webFilter.setServerAuthenticationConverter(new XTAuthenticationConverter());
}
}
return chain;
}
@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
return new ReactiveAuthenticationManagerAdapter((authentication)->{
if(authentication instanceof XTAccountAuthentication) {
XTAccountAuthentication gmAccountAuthentication = (XTAccountAuthentication) authentication;
if(gmAccountAuthentication.getPrincipal() != null) {
authentication.setAuthenticated(true);
return authentication;
} else {
return authentication;
}
} else {
return authentication;
}
});
}
}
(4).取出token中的权限信息
/**
* @description: 取出token中的权限信息
* @author: ※狗尾巴草
* @date: 2020-11-27 23:12
**/
public class XTAuthenticationConverter extends ServerFormLoginAuthenticationConverter {
private static Logger logger = LoggerFactory.getLogger(XTAuthenticationConverter.class);
@Override
public Mono<Authentication> convert(ServerWebExchange exchange) {
System.out.println("从token中获取登陆用户信息");
//从token中获取登陆用户信息
List<String> tokenList = exchange.getRequest().getHeaders().get("token");
if(tokenList==null) {
logger.error("用户认证信息为空,返回获取认证信息失败");
return Mono.empty();
} else {
String token = tokenList.get(0);
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
//获取权限信息
if(!JwtTokenUtils.checkJWT(token)){
logger.error("用户认证信息为空,返回获取认证信息失败");
return Mono.empty();
}
List<String> roles = JwtTokenUtils.getUserRole(token);
if(roles==null){
System.out.println("token过期");
return Mono.empty();
}
roles.forEach(role ->{
SimpleGrantedAuthority auth = new SimpleGrantedAuthority(role);
simpleGrantedAuthorities.add(auth);
});
//添加用户信息到spring security之中。
XTAccountAuthentication xinyueAccountAuthentication = new XTAccountAuthentication(null, token, simpleGrantedAuthorities);
return Mono.just(xinyueAccountAuthentication);
}
}
}
XTAccountAuthentication类
/**
* @description: 说明
* @author: ※狗尾巴草
* @date: 2020-11-27 23:13
**/
public class XTAccountAuthentication extends AbstractAuthenticationToken {
private static final long serialVersionUID = 1L;
private Object credentials;
private String principal;
public XTAccountAuthentication(Object credentials,String principal,Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.credentials = credentials;
this.principal = principal;
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public String getPrincipal() {
return this.principal;
}
}
(5).鉴权
/**
* @description: 鉴权
* @author: ※狗尾巴草
* @date: 2020-11-27 21:46
**/
public class XTReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
private List<String> authorities = new ArrayList<>();
public XTReactiveAuthorizationManager(String authority, String... authorities ) {
this.authorities.add("ROLE_" + authority);
if(authorities != null) {
for(String auth : authorities) {
this.authorities.add("ROLE_" + auth);
}
}
}
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
return authentication
.filter(a -> a.isAuthenticated())
.flatMapIterable(a -> a.getAuthorities())
.map(g -> g.getAuthority())
.any(c -> {
//检测权限是否匹配
System.out.println("角色: " + c);
if (authorities.contains(c)) {
return true;
}
ResponseData<Object> data = new ResponseData<>().fail(ResultCode.NO_PERMISSION.getCode(),ResultCode.NO_PERMISSION.getMessage());
//权限不足,抛出异常
throw new AccessDeniedException(JSONObject.toJSONString(data));
})
.map(hasAuthority -> new AuthorizationDecision(hasAuthority))
.defaultIfEmpty(new AuthorizationDecision(false));
}
}
(6).统一异常捕获
/**
* @description: 网关异常通用处理器,只作用在webflux 环境下
* @author: ※狗尾巴草
* @date: 2020-11-28 16:28
**/
@Order(-1)
@RequiredArgsConstructor
@Component
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
System.out.println("发生异常");
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
//设置响应头的数据类型,applicaion/json模式,返回json数据
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response
.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(ex.getMessage()));
} catch (JsonProcessingException e) {
// log.warn("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
(7).无权限处理
/**
* @description: 匿名用户访问无权限资源时的异常
* @author: ※狗尾巴草
* @date: 2020-11-13 14:15
**/
@Component
public class CustomizeAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
System.out.println("无权限访问");
ResponseData<Object> data = new ResponseData<>().fail(ResultCode.NO_PERMISSION.getCode(),ResultCode.NO_PERMISSION.getMessage());
// 抛出异常
return Mono.error(new AccessDeniedException(JSONObject.toJSONString(data)));
}
}
3.认证中心
注:
统一认证中心,统一登录入口,同一套规则,但是管理员数据和普通用户数据还是分表存储,暂时没找到好的办法解决,这里使用简单的方法区别管理员登录和普通用户登录,增加前缀,
例如
管理员登录,admin_111
用户登录: user_111
(1).依赖
<dependencies>
<!--公共模块-->
<dependency>
<groupId>com.qiongqi.taoyiquan</groupId>
<artifactId>taoyiquan-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</exclusion>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--springcloud整合的openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
(2).yml配置
server:
port: 8850
servlet:
context-path: /api-auth
spring:
application:
name: taoyiquan-auth
cloud:
nacos:
discovery:
server-addr: localhost:8848
(3).Security配置
/**
* @description: Security配置
* @author: ※狗尾巴草
* @date: 2020-11-12 12:25
**/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomizeAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private CustomizeAuthenticationSuccessHandler successHandler;
@Autowired
private CustomizeAuthenticationFailureHandler failureHandler;
@Autowired
private CustomizeLogoutSuccessHandler logoutSuccessHandler;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 设置默认的加密方式(强hash方式加密)
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
//获取用户账号密码及权限信息
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//配置认证方式
auth.userDetailsService(userDetailsService());
}
// @Override
// public void configure(WebSecurity web) {
// //对于在header里面增加token等类似情况,放行所有OPTIONS请求。
// web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
// }
@Override
protected void configure(HttpSecurity http) throws Exception {
//http相关的配置,包括登入登出、异常处理、会话管理等
http
.authorizeRequests()
// .antMatchers("/user/**").hasAuthority("query_user")
// 基于角色控制
// .antMatchers("/admin/**").hasAnyRole("admin")
// .antMatchers("/user/**").hasAnyRole("user")
//登入
.and()
.formLogin()
.loginPage("/v1/login")
.permitAll()//允许所有用户
.successHandler(successHandler)//登录成功处理逻辑
.failureHandler(failureHandler)//登录失败处理逻辑
// //登出
.and().logout()
.permitAll()//允许所有用户
.logoutSuccessHandler(logoutSuccessHandler)//登出成功处理逻辑
.deleteCookies("JSESSIONID")//登出之后删除cookie
//异常处理(权限拒绝、登录失效等)
.and().exceptionHandling().
authenticationEntryPoint(authenticationEntryPoint)//匿名用户访问无权限资源时的异常处理
// .accessDeniedHandler(accessDeniedHandler)//异常捕获
// .authenticationEntryPoint(new JWTAuthenticationEntryPoint())
// 限制同一账号只能一个用户使用 会话管理
.and().sessionManagement()
// .maximumSessions(1)//同一账号同时登录最大用户数
// .expiredSessionStrategy(sessionInformationExpiredStrategy)//会话信息过期策略会话信息过期策略(账号被挤下线)
;
http.csrf().disable().cors()
.and()
// .addFilter(new JWTAuthenticationFilter(authenticationManager()))
// .addFilter(new JWTAuthorizationFilter(authenticationManager()))
// 不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
// http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);//增加到默认拦截链中
}
}
(4).登录认证授权
/**
* @description: 登录认证授权
* @author: ※狗尾巴草
* @date: 2020-11-13 10:00
**/
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private adminFeignClient adminFeignClient;
@Autowired
private roleFeignClient roleFeignClient;
@Autowired
private userFeignClient userFeignClient;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//需要构造出 org.springframework.security.core.userdetails.User 对象并返回
//校验验证码等操作....
//
if(!StringUtils.isNotBlank(username)){
throw new RuntimeException("用户不能为空");
}
ResponseData Account =null;
String[] name_list = username.split("_");
if("user".equals(name_list[0])){
// 普通用户登录
Account = userFeignClient.getByAccount(name_list[1]);
}else if("admin".equals(name_list[0])){
// 管理员登录
Account = adminFeignClient.getByAccount(name_list[1]);
}
LinkedHashMap data = (LinkedHashMap) Account.getData();
if(data==null){
throw new RuntimeException("用户不存在");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//获取该用户所拥有的角色
ResponseData roleData = roleFeignClient.getByUserId(data.get("id").toString());
JSONObject roleJson =(JSONObject) JSONObject.toJSON(roleData);
//角色和权限共用GrantedAuthority接口,唯一的不同角色就是多了个前缀"ROLE_"
// // 声明用户角色
roleJson.getJSONArray("data").forEach(role ->{
//基于角色控制
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+((JSONObject)role).get("roleCode")));
});
User user = new User(data.get("account").toString(), data.get("password").toString(), true, true, true, true, grantedAuthorities);
return user;
}
}
(5).登录成功处理器
/**
* @description: 登录成功处理器
* @author: ※狗尾巴草
* @date: 2020-11-13 14:19
**/
@Component
public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private adminFeignClient adminFeignClient;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
//更新用户表上次登录时间、更新人、更新时间等字段
User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
adminFeignClient.updateLoginTime(userDetails.getUsername(),new Date());
// SysUserEntity sysUser = sysUserService.selectByName(userDetails.getUsername());
// sysUser.setLast_login_time(new Date());
// sysUser.setUpdate_time(new Date());
// sysUser.setUpdate_user(sysUser.getId());
// sysUserService.updateById(sysUser);
//此处还可以进行一些处理,比如登录成功之后可能需要返回给前台当前用户有哪些菜单权限,
//进而前台动态的控制菜单的显示等,具体根据自己的业务需求进行扩展
// 获取用户权限
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
List<String> list = new ArrayList<>();
for (GrantedAuthority authority : authorities){
list.add(authority.getAuthority());
}
//将权限放入token中
String token = JwtTokenUtils.createToken(userDetails.getUsername(), list);
JSONObject object = new JSONObject();
object.put("token",token);
object.put("account",userDetails.getUsername());
//处理编码方式,防止中文乱码的情况
httpServletResponse.setContentType("text/json;charset=utf-8");
//塞到HttpServletResponse中返回给前台
httpServletResponse.getWriter().write(JSON.toJSONString(object));
}
}
(6).登录失败
/**
* @description: 登录失败处理逻辑
* @author: ※狗尾巴草
* @date: 2020-11-13 14:22
**/
@Component
public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//返回json数据
ResponseData<Object> data = null;
if (e instanceof AccountExpiredException) {
//账号过期
data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_EXPIRED.getCode(),ResultCode.USER_ACCOUNT_EXPIRED.getMessage());
} else if (e instanceof BadCredentialsException) {
//密码错误
data = new ResponseData<>().fail(ResultCode.USER_CREDENTIALS_ERROR.getCode(),ResultCode.USER_CREDENTIALS_ERROR.getMessage());
} else if (e instanceof CredentialsExpiredException) {
//密码过期
data = new ResponseData<>().fail(ResultCode.USER_CREDENTIALS_EXPIRED.getCode(),ResultCode.USER_CREDENTIALS_EXPIRED.getMessage());
} else if (e instanceof DisabledException) {
//账号不可用
data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_DISABLE.getCode(),ResultCode.USER_ACCOUNT_DISABLE.getMessage());
} else if (e instanceof LockedException) {
//账号锁定
data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_LOCKED.getCode(),ResultCode.USER_ACCOUNT_LOCKED.getMessage());
} else if (e instanceof InternalAuthenticationServiceException) {
//用户不存在
data = new ResponseData<>().fail(ResultCode.USER_ACCOUNT_NOT_EXIST.getCode(),ResultCode.USER_ACCOUNT_NOT_EXIST.getMessage());
}else{
//其他错误
data = new ResponseData<>().fail(ResultCode.COMMON_FAIL.getCode(),ResultCode.COMMON_FAIL.getMessage());
}
//处理编码方式,防止中文乱码的情况
httpServletResponse.setContentType("text/json;charset=utf-8");
//塞到HttpServletResponse中返回给前台
httpServletResponse.getWriter().write(JSON.toJSONString(data));
}
}
7.微服务调用
这里使用Feign做服务调用,
1.admin服务调用
/**
* @description: admin服务调用
* * 声明需要调用的微服务名称
* * @FeignClient
* * * name : 服务提供者的名称
* @author: ※狗尾巴草
* @date: 2020-11-27 11:20
**/
@FeignClient(name="taoyiquan-admin",contextId = "admin")
public interface adminFeignClient {
@GetMapping("/api-admin/v1/admin/entity/getByAccount")
ResponseData getByAccount(@RequestParam("account") String account);
@GetMapping("/api-admin/v1/admin/entity/updateLoginTime")
ResponseData updateLoginTime(@RequestParam("account") String account, @RequestParam("loginTime") Date loginTime);
}
2.其他服务调用参考源码,
管理员服务
注:管理员服务,用户服务等无特殊逻辑实现,跟平常些项目一样增删改查操作
(省略一堆废话…)
五.功能测试
1.普通用户登录
接口; http://localhost:8849/auth/api-auth/v1/login
(1).访问接口
(a).用户资源接口-app接口
http://localhost:8849/user/api-user/v1/api/entity
(b).用户资源接口-admin接口
http://localhost:8849/user/api-user/v1/admin/entity
( c) .管理员接口
http://localhost:8849/admin/api-admin/v1/admin/entity
(d).公共资源接口
http://localhost:8849/taobao/api-taobao/v1/info
2.管理员登录
http://localhost:8849/auth/api-auth/v1/login?username=admin_111&password=111
1.访问接口
(a).用户资源接口-app接口
http://localhost:8849/user/api-user/v1/api/entity
(b).用户资源接口-admin接口
http://localhost:8849/user/api-user/v1/admin/entity
(a).访问管理员接口
http://localhost:8849/admin/api-admin/v1/admin/entity
六:最后:
想象中的功能设计:
本来想在gateway的全局过滤器中拦截指定接口,再其判断token的有效性,有效即可通过,去鉴权,无效就拦截
现实中的功能实现(我所能实现的):
添加完成security和全局过滤器后,发现奇怪的事情,就是security执行在先,过滤器执行再后,无论过滤器的Order设置多小(记得是Order越小,优先级越高,先执行),都是过滤器执行再后,无奈就将过滤器注释掉,
七.项目地址
大体功能写到这,细节未完善
有哪位路过的大佬,还望不吝赐教
补充:之前忘记备份数据库,后来系统重装,一夜回到解放前…
根据实体类补充了数据库,已上传github,还没测(目测基本不用测,哈哈)