1.我们将表单传过来的用户名称和密码封装成UsernamePasswordAuthenticationToken对象,而UsernamePasswordAuthenticationToken又继承了AbstractAuthenticationToken,而AbstractAuthenticationToken又是Authentication的实现类,因此我们满足了封装成Authentication对象的条件,可以往后进行authenticate()方法的认证 2.在authenticate()方法的认证的过程中,我们分析,先通过ProviderManager调DaoAuthenticationProvider的authentication()进行认证。而DaoAuthenticationProvider又会去调用UserDetialService当中的loadUserByUsername方法查询用户。 3.我们默认的实现类是InMemoryUserDetailsManager,意思是从内存中查询用户,它内部拥有自己的UserDetail实现类对象,封装的是我们配置的用户值亦或是SpringSecurity默认的用户值。而我们的开发需求肯定是从数据库中查询用户信息,而后封装成UserDetial对象.因此我们要自定义UserDetailService的实现类以及要自定义UserDetial的实现类 原文链接:https://blog.csdn.net/m0_69261651/article/details/129506369 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>3.0.5</version> </dependency>
gateway 实现filter
1.思路
(1)开放白名单url
(2)非白名单通过自定义方法判断是否为系统用户
(3)取出某些字段方便接口调用直接从header中获取
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String url = request.getPath().value(); url = org.apache.commons.lang.StringUtils.removeStart(url, "/api"); String path = url.substring(url.indexOf("/", 1)); //请求路径在访问白名单中则放行 PathMatcher pathMatcher = new AntPathMatcher(); if (Stream.of(this.urlwhitelist.split(",")).anyMatch(value -> pathMatcher.match(value, path))) { return chain.filter(exchange); } String token = request.getHeaders().getFirst(CommonConstant.TOKEN); String userType = request.getHeaders().getFirst(CommonConstant.userType); String method = request.getMethodValue(); String remoteAddr = gainIPUtil.getIpAddress(request); log.info("url:{},ip:{},method:{},headers:{}", url, remoteAddr, method, request.getHeaders()); //token为空 if (StringUtils.isBlank(token)) { return loginTimeout(exchange); } //校验token及权限信息-----------实现方法在这里 ApproveVo approveVo = authService.checktoken(token, path, remoteAddr, userType); if (null == approveVo || StringUtils.isEmpty(approveVo.getUserId())){ return unauthorized(exchange); } //增加权限参数 request.mutate().header("tenantId",approveVo.getTrnantId()) .header("userId",approveVo.getUserId()) .header("superAdmin",approveVo.getSuperAdmin()).build(); if (approveVo.getSuperAdmin().equals(CommonConstant.SYSTEM_ROLE_YES)){ request.mutate().header("dataScope",CommonConstant.SYSTEM_ROLE_YES).build(); }else { request.mutate().header("dataScope",approveVo.getDataScope()).build(); } return chain.filter(exchange); }
auth服务完成登录逻辑
1.loadUserByUsername方法
UserDetailsService接口里有且仅有一个方法,它就是及其巧妙的loadUserByUsername()
,为什么说它巧妙?答曰:见名知意,功能甚好。它通过用户名加载用户信息到Security内置容器UserDetails中,认证时通过注入的形式需要的提供数据。
重写它的loadUserByUsername方法,在里面自定义登陆逻辑(通常来说:通过用户名查询数据库,获取到用户对象信息)。
UserDetails
把这些信息取出来,然后包装成一个对象交由框架去认证,认证包括密码、是否过期、是否锁定、权限等等。
loadUserByUsername的回参是UserDetails接口类型的数据。查看UserDetails源码,它里面有很多的方法,方法名称就是对功能的最简单的描述
package org.springframework.security.core.userdetails; 实现UserDetailsService
@Service public class UserDetailsServiceImpl implements UserDetailsService { //根据自己业务需要定义查询接口 @Resource private ManagementService managementService; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //TODO //查询用户信息 根据自己业务需要定义查询接口 TokenUserVo userName = managementService.findByUserName(s); if (null == userName || StringUtils.isEmpty(userName.getId())){ throw new BasicException(ResultCode.USER_NOT_EXIST); } TenantUserEntity tenantUserEntity = new TenantUserEntity(); BeanUtils.copyProperties(userName, tenantUserEntity); return tenantUserEntity; } }
TenantUserEntity
TenantUserEntity 是UserDetails的默认实现类,把UserDetails里面的方法中待获取的数据实际的构造出来。
@Data @NoArgsConstructor @AllArgsConstructor public class TenantUserEntity implements UserDetails, Serializable { /** * 用户id */ private String id; /** * 租户ID */ private String tenantId; /** * 是否为系统管理租户 */ private String SysTenant; /** * 是否为系统管理租户 */ private String SysUser; /** * 过期时间 */ @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd", timezone = "GMT+8") private LocalDate expireTime; /** * 用户平台(后台管理系统:sys,小程序:mini,移动端:app,多平台用逗号连接) */ private String userType; /** * 用户名 */ private String userName; /** * 密码 */ private String passWord; /** * 手机号 */ private String phone; /** * 状态:(0:禁用,1:正常) */ private Boolean status; /** * 创建人 */ private String createBy; /** * 组织机构ID */ private Long organizationId; /** * 岗位id */ private Long postId; /** * 角色id */ private Long roleId; /** * 权限集合(用来存从数据库查到的用户权限)) */ private List<String> permissions; //忽略序列化 SpringSecrity权限校验需要的格式的权限集合 @JSONField(serialize = false) private List<SimpleGrantedAuthority> authorities; @Override public Collection<? extends GrantedAuthority> getAuthorities() { if (!CollectionUtils.isEmpty(authorities)){ return authorities; } //把 permissions 中的String类型权限信息,封装成SimpleGrantedAuthority对象 authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); return authorities; } @Override public String getPassword() { return passWord; } @Override public String getUsername() { return userName; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return status; } }
AuthenticationManager
用来认证
//AuthenticationManager authenticate 进行用户认证 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getUserName(),password); Authentication authenticate; try { authenticate = authenticationManager.authenticate(authenticationToken);