Commit 45d54729 by 袁伟铭

sys-server新增单点登录

parent 2d140d0a
......@@ -25,7 +25,7 @@ public class ContextUtils {
public static Long getUserUserId() {
ApiTokenVo apiTokenVo = ThreadContext.get(APP_TOKEN_CONTEXT_KEY);
return apiTokenVo.getUserId();
return apiTokenVo == null ? null : apiTokenVo.getUserId();
}
public static void setAdminContext(OnlineUserDto onlineUserDto) {
......@@ -40,7 +40,7 @@ public class ContextUtils {
public static Long getAdminUserId() {
OnlineUserDto userDto = ThreadContext.get(ADMIN_TOKEN_CONTEXT_KEY);
return userDto.getUserId();
return userDto == null ? null : userDto.getUserId();
}
}
......
package com.zq.common.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 用户表(TAppUser)实体类
*
* @author makejava
* @since 2021-06-22 15:39:45
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName(value = "t_app_user")
public class AppUser {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 手机号
*/
@ApiModelProperty("手机号")
private String phone;
/**
* 登录密码
*/
@ApiModelProperty("登录密码")
private String password;
/**
* 交易密码
*/
@ApiModelProperty("交易密码")
private String dealpwd;
/**
* 用户类型
*/
@ApiModelProperty("用户类型 ")
private Integer userType;
/**
* 名字
*/
@ApiModelProperty("名字")
private String name;
/**
* 昵称
*/
@ApiModelProperty("昵称")
private String nickname;
/**
* 头像
*/
@ApiModelProperty("头像")
private String avatar;
/**
* 性别
*/
@ApiModelProperty("性别")
private String sex;
/**
* 年龄
*/
@ApiModelProperty("年龄")
private Integer age;
/**
* 地址
*/
@ApiModelProperty("地址")
private String address;
/**
* 状态:0正常 1冻结 2删除
*/
@ApiModelProperty("状态:0正常 1冻结 2删除")
private Integer status;
/**
* 最后登录访问IP
*/
@ApiModelProperty("最后登录访问IP")
private String accessIp;
/**
* 区域码
*/
@ApiModelProperty("区域码")
private String areaId;
/**
* 开放平台获取的unionid,解决这个同一个企业的不同APP和不同公众号之间的帐号共通
*/
@ApiModelProperty("开放平台获取的unionid,解决这个同一个企业的不同APP和不同公众号之间的帐号共通")
private String unionId;
/**
* 最后登录时间
*/
@ApiModelProperty("最后登录时间")
private Date lastLoginTime;
/**
* createTime
*/
private Date createTime;
/**
* updateTime
*/
private Date updateTime;
}
......@@ -44,7 +44,6 @@
<module>resource-server</module>
<module>open-server</module>
<module>oauth-server</module>
<module>single-server</module>
</modules>
<profiles>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>civil-bigdata</artifactId>
<groupId>com.zq</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>single-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.zq</groupId>
<artifactId>civil-common-utils</artifactId>
<version>1.0.0</version>
</dependency>
<!-- &lt;!&ndash; 注册中心 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; 连接配置中心 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-config</artifactId>-->
<!-- </dependency>-->
<!--Spring boot 测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Spring boot Web容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring boot 安全框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Spring devtools 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<!--Spring boot Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!--Mybatis plus多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!-- druid数据源驱动 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${alibaba.druid.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- oracle8 -->
<dependency>
<groupId>org.oracle</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.1.0.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.database.nls/orai18n -->
<dependency>
<groupId>com.oracle.database.nls</groupId>
<artifactId>orai18n</artifactId>
<version>21.1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 使用@profiles.active@需要添加以下内容 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<!--开启过滤,用指定的参数替换directory下的文件中的参数-->
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
\ No newline at end of file
package com.zq.single;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author wilmiam
* @since 2021/11/22 10:14
*/
@EnableScheduling
@MapperScan("com.zq.single.mapper")
// @EnableDiscoveryClient
@SpringBootApplication(scanBasePackages = {"com.zq.single", "com.zq.common.config"})
public class SingleApplication {
public static void main(String[] args) {
SpringApplication.run(SingleApplication.class, args);
}
}
package com.zq.single;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author wilmiam
* @since 2021-07-29 11:32
*/
@Configuration
@EnableSwagger2
public class Swagger {
@Value("${spring.profiles.active}")
private String profile;
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("admin")
.enable(!"product".equals(profile)) //生产环境关闭
.select()
.apis(RequestHandlerSelectors.basePackage("com.zq.single.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("后台接口文档")
.description("查看接口文档")
.build();
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zq.single.config;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author Zheng Jie
*/
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
//当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zq.single.config;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author Zheng Jie
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException == null ? "Unauthorized" : authException.getMessage());
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zq.single.config;
import com.zq.common.annotation.AnonymousAccess;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.utils.RequestMethodEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.*;
/**
* @author Zheng Jie
*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenProvider tokenProvider;
private final JwtAuthenticationEntryPoint authenticationErrorHandler;
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final ApplicationContext applicationContext;
private final SecurityProperties properties;
private final RedisUtils redisUtils;
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url: @AnonymousAccess
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获取匿名标记
Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);
Set<String> allType = anonymousUrls.get(RequestMethodEnum.ALL.getType());
//不使用注解的时候在这添加url放行
allType.add("/single/**");
httpSecurity
// 禁用 CSRF
.csrf().disable()
// .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.exceptionHandling()
.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler)
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
// 不创建会话
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 静态资源等等
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/webSocket/**"
).permitAll()
// swagger 文档
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll()
// 文件
.antMatchers("/avatar/**").permitAll()
.antMatchers("/file/**").permitAll()
// 阿里巴巴 druid
.antMatchers("/druid/**").permitAll()
// 放行OPTIONS请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型
// GET
.antMatchers(HttpMethod.GET, anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
// POST
.antMatchers(HttpMethod.POST, anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
// PUT
.antMatchers(HttpMethod.PUT, anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
// PATCH
.antMatchers(HttpMethod.PATCH, anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
// DELETE
.antMatchers(HttpMethod.DELETE, anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
// 所有类型的接口都放行
.antMatchers(allType.toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated()
.and().apply(securityConfigurerAdapter());
}
private TokenConfigurer securityConfigurerAdapter() {
return new TokenConfigurer(tokenProvider, properties, redisUtils);
}
private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {
Map<String, Set<String>> anonymousUrls = new HashMap<>(6);
Set<String> get = new HashSet<>();
Set<String> post = new HashSet<>();
Set<String> put = new HashSet<>();
Set<String> patch = new HashSet<>();
Set<String> delete = new HashSet<>();
Set<String> all = new HashSet<>();
for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
RequestMethodEnum request = RequestMethodEnum.find(requestMethods.size() == 0 ? RequestMethodEnum.ALL.getType() : requestMethods.get(0).name());
switch (Objects.requireNonNull(request)) {
case GET:
get.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case POST:
post.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PUT:
put.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case PATCH:
patch.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
case DELETE:
delete.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
default:
all.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
break;
}
}
}
anonymousUrls.put(RequestMethodEnum.GET.getType(), get);
anonymousUrls.put(RequestMethodEnum.POST.getType(), post);
anonymousUrls.put(RequestMethodEnum.PUT.getType(), put);
anonymousUrls.put(RequestMethodEnum.PATCH.getType(), patch);
anonymousUrls.put(RequestMethodEnum.DELETE.getType(), delete);
anonymousUrls.put(RequestMethodEnum.ALL.getType(), all);
return anonymousUrls;
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zq.single.config;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @author /
*/
@RequiredArgsConstructor
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;
private final SecurityProperties properties;
private final RedisUtils redisUtils;
@Override
public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenProvider, properties, redisUtils);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zq.single.config;
import cn.hutool.core.util.StrUtil;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.context.ContextUtils;
import com.zq.common.vo.OnlineUserDto;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Objects;
/**
* @author /
*/
public class TokenFilter extends GenericFilterBean {
private static final Logger log = LoggerFactory.getLogger(TokenFilter.class);
private final TokenProvider tokenProvider;
private final SecurityProperties properties;
private final RedisUtils redisUtils;
/**
* @param tokenProvider Token
* @param properties JWT
* @param redisUtils redis
*/
public TokenFilter(TokenProvider tokenProvider, SecurityProperties properties, RedisUtils redisUtils) {
this.properties = properties;
this.tokenProvider = tokenProvider;
this.redisUtils = redisUtils;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String token = resolveToken(httpServletRequest);
// 对于 Token 为空的不需要去查 Redis
if (StrUtil.isNotBlank(token)) {
OnlineUserDto onlineUserDto = null;
boolean cleanUserCache = false;
try {
onlineUserDto = redisUtils.getObj(properties.getOnlineKey() + token, OnlineUserDto.class);
} catch (ExpiredJwtException e) {
log.error(e.getMessage());
cleanUserCache = true;
} finally {
if (cleanUserCache || Objects.isNull(onlineUserDto)) {
// userCacheClean.cleanUserCache(String.valueOf(tokenProvider.getClaims(token).get(TokenProvider.AUTHORITIES_KEY)));
}
}
if (onlineUserDto != null && StringUtils.isNotBlank(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Token 续期
tokenProvider.checkRenewal(token);
// 设置当前用户
ContextUtils.setAdminContext(onlineUserDto);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
/**
* 初步检测Token
*
* @param request /
* @return /
*/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(properties.getHeader());
if (StringUtils.isBlank(bearerToken)) {
return null;
}
if (bearerToken.startsWith(properties.getTokenStartWith())) {
// 去掉令牌前缀
return bearerToken.replace(properties.getTokenStartWith(), "");
} else {
log.debug("非法Token:{}", bearerToken);
}
return null;
}
}
package com.zq.single.config;/*
* Copyright 2019-2020 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author /
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TokenProvider implements InitializingBean {
private final RedisUtils redisUtils;
private final SecurityProperties properties;
public static final String AUTHORITIES_KEY = "auth";
private static Key key;
private static SignatureAlgorithm signatureAlgorithm;
@Override
public void afterPropertiesSet() {
signatureAlgorithm = SignatureAlgorithm.HS512;
byte[] keyBytes = DatatypeConverter.parseBase64Binary(properties.getBase64Secret());
key = new SecretKeySpec(keyBytes, signatureAlgorithm.getJcaName());
}
public static String createToken(Authentication authentication) {
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(signatureAlgorithm, key)
// 加入ID确保生成的 Token 都不一致
.setId(IdUtil.simpleUUID())
.compact();
}
public Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(properties.getBase64Secret()))
.parseClaimsJws(token)
.getBody();
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(properties.getBase64Secret()))
.parseClaimsJws(token)
.getBody();
// fix bug: 当前用户如果没有任何权限时,在输入用户名后,刷新验证码会抛IllegalArgumentException
Object authoritiesStr = claims.get(AUTHORITIES_KEY);
Collection<? extends GrantedAuthority> authorities =
ObjectUtil.isNotEmpty(authoritiesStr) ?
Arrays.stream(authoritiesStr.toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList()) : Collections.emptyList();
User principal = new User(claims.getSubject(), "******", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
/**
* @param token 需要检查的token
*/
public void checkRenewal(String token) {
// 判断是否续期token,计算token的过期时间
long time = redisUtils.getExpire(properties.getOnlineKey() + token) * 1000;
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
// 判断当前时间与过期时间的时间差
long differ = expireDate.getTime() - System.currentTimeMillis();
// 如果在续期检查的范围内,则续期
if (differ <= properties.getDetect()) {
long renew = time + properties.getRenew();
redisUtils.expire(properties.getOnlineKey() + token, renew, TimeUnit.MILLISECONDS);
}
}
public String getToken(HttpServletRequest request) {
String bearerToken = request.getHeader(properties.getHeader());
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(properties.getTokenStartWith())) {
// 去掉令牌前缀
return bearerToken.replace(properties.getTokenStartWith(), "");
}
return null;
}
}
package com.zq.single.config;
import com.zq.single.config.interceptor.ApiInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@RequiredArgsConstructor
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final ApiInterceptor apiInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(apiInterceptor)
.addPathPatterns("/single/**")
// .excludePathPatterns("/xxxx/**")
;
}
}
package com.zq.single.config.interceptor;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.zq.common.config.redis.BaseCacheKeys;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.context.ContextUtils;
import com.zq.common.vo.ApiTokenVo;
import com.zq.common.vo.ResultVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Component
@RequiredArgsConstructor
public class ApiInterceptor implements HandlerInterceptor {
private final RedisUtils redisUtils;
private final SecurityProperties properties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = ServletUtil.getClientIP(request);
log.debug("{}请求URI: {}", ip, request.getRequestURL());
String token = request.getHeader(properties.getHeader());
ApiTokenVo tokenVo = redisUtils.getObj(BaseCacheKeys.appTokenKey(token), ApiTokenVo.class);
if (tokenVo == null) {
response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
ServletUtil.write(response, JSONUtil.toJsonStr(ResultVo.fail(401, "认证失败")), ContentType.JSON.getValue());
return false;
}
ContextUtils.setUserContext(tokenVo);
return true;
}
}
package com.zq.single.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zq.common.annotation.AnonymousAccess;
import com.zq.common.config.redis.BaseCacheKeys;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.ApiTokenUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.vo.ApiTokenVo;
import com.zq.common.vo.ResultVo;
import com.zq.single.entity.CasSystem;
import com.zq.single.entity.CasUserInfo;
import com.zq.single.entity.SysUser;
import com.zq.single.service.ICasSystemService;
import com.zq.single.service.ICasUserInfoService;
import com.zq.single.service.ISysUserService;
import com.zq.single.vo.UserInfoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Api(tags = "登录认证相关接口")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/single/auth")
public class AuthController {
private final ISysUserService iSysUserService;
private final ICasSystemService iCasSystemService;
private final ICasUserInfoService iCasUserInfoService;
private final RedisUtils redisUtils;
private final SecurityProperties properties;
@RequestMapping("/login")
@AnonymousAccess
public ResultVo login(@RequestBody SysUser sysUser, HttpServletRequest request, HttpServletResponse response) throws IOException {
LambdaQueryWrapper<SysUser> sysUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
sysUserLambdaQueryWrapper.eq(SysUser::getUsername, sysUser.getUsername());
List<SysUser> users = iSysUserService.list(sysUserLambdaQueryWrapper);
// AssertUtils.isTrue(users != null && users.size() > 0 && users.get(0).getPassword().equals(sysUser.getPassword()), "用户名或密码错误");
SysUser thisUser = users.get(0);
//业务系统Id
String systemId = request.getParameter("systemId");
String serverDomain = "";
if (StrUtil.isNotBlank(systemId)) {
CasSystem byId = iCasSystemService.getById(Long.parseLong(systemId));
if (byId != null) {
serverDomain = byId.getSystemInfo();
}
}
//查出该用户绑定业务系统的信息
LambdaQueryWrapper<CasUserInfo> casUserInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
casUserInfoLambdaQueryWrapper.eq(CasUserInfo::getUserId, thisUser.getUserId());
List<CasUserInfo> infoList = iCasUserInfoService.list(casUserInfoLambdaQueryWrapper);
UserInfoVo userToken = new UserInfoVo();
userToken.setUserId(thisUser.getUserId());
userToken.setUsername(thisUser.getUsername());
List<Map<String, String>> sonInfoList = new ArrayList<>();
for (CasUserInfo casUserInfo : infoList) {
Map<String, String> map = new HashMap<>();
map.put("systemName", casUserInfo.getSystemName());
map.put("systemUserId", casUserInfo.getSystemUserId().toString());
map.put("systemUserName", casUserInfo.getSystemUserName());
sonInfoList.add(map);
}
userToken.setSonInfoList(sonInfoList);
/* ApiTokenVo tokenVo = ApiTokenVo.builder()
.userId(thisUser.getUserId())
.build();*/
String token = ApiTokenUtils.createToken(userToken, BaseCacheKeys.APP_TOKEN_EXPIRE_MINUTES);
response.setHeader(properties.getHeader(), token);
redisUtils.setObj(BaseCacheKeys.appTokenKey(token), userToken, BaseCacheKeys.APP_TOKEN_EXPIRE_MINUTES);
/* String redirectUrl = "http://192.168.11.103:8080/#/login?" + properties.getHeader() + "=" + token + "&JSESSIONID=" + request.getSession().getId();
// response.sendRedirect("" + "/#/verifyLogin?" + properties.getHeader() + "=" + token + "&JSESSIONID=" + request.getSession().getId());
response.sendRedirect("http://192.168.11.103:8080/#/manage?" + properties.getHeader() + "=" + token + "&JSESSIONID=" + request.getSession().getId());*/
return ResultVo.success();
}
@ApiOperation("获取用户信息")
@GetMapping(value = "/info")
public ResultVo<Object> getUserInfo(HttpServletRequest request) {
String token = request.getHeader(properties.getHeader());
if (StringUtils.isNotBlank(token)) {
token= token.replace(properties.getTokenStartWith(), "");
}
Object obj = redisUtils.getObj(BaseCacheKeys.appTokenKey(token));
return ResultVo.success(obj);
}
}
package com.zq.single.controller;
import cn.hutool.core.util.IdcardUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zq.common.annotation.AnonymousAccess;
import com.zq.common.config.redis.BaseCacheKeys;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.ApiTokenUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.context.ContextUtils;
import com.zq.common.utils.AssertUtils;
import com.zq.common.vo.OnlineUserDto;
import com.zq.common.vo.ResultVo;
import com.zq.single.entity.CasUserInfo;
import com.zq.single.entity.SysUser;
import com.zq.single.service.ISysUserService;
import com.zq.single.vo.CasUserInfoVo;
import com.zq.single.vo.UserInfoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 系统用户 前端控制器
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Api(value = "SysUserController", tags = "账号管理")
@RestController
@RequiredArgsConstructor
@RequestMapping("/single/user")
public class SysUserController {
@Autowired
private ISysUserService sysUserService;
private final RedisUtils redisUtils;
private final SecurityProperties properties;
@ApiOperation("注册查用户名是否已存在")
@AnonymousAccess
@GetMapping("/userNameIsExist/{userName}")
public ResultVo userNameIsExist(@PathVariable("userName") String userName) {
Boolean isSuccess = sysUserService.userNameIsExist(userName);
AssertUtils.isTrue(isSuccess, "用户名已存在");
return ResultVo.success();
}
@ApiOperation("注册用户")
@AnonymousAccess
@PostMapping("/register")
public ResultVo register(@RequestBody SysUser sysUser) {
/*String password= MD5PasswordEncoderUtil.encode(sysUser.getPassword());
sysUser.setPassword(password.trim());*/
Boolean isSuccess = sysUserService.save(sysUser);
AssertUtils.isTrue(isSuccess, "服务器繁忙请稍后再试");
return ResultVo.success();
}
@ApiOperation("更改用户信息")
@PostMapping("/update")
public ResultVo update(@RequestBody SysUser sysUser) {
/* String password= MD5PasswordEncoderUtil.encode(sysUser.getPassword().trim());
sysUser.setPassword(password);*/
Boolean isSuccess = sysUserService.updateById(sysUser);
AssertUtils.isTrue(isSuccess, "服务器繁忙请稍后再试");
return ResultVo.success();
}
@ApiOperation("删除用户信息")
@PostMapping("/delete/{userId}")
public ResultVo delete(@PathVariable("userId") Long userId) {
SysUser sysUser=new SysUser();
sysUser.setUserId(userId);
sysUser.setEnabled(0L);
Boolean isSuccess = sysUserService.updateById(sysUser);
AssertUtils.isTrue(isSuccess, "服务器繁忙请稍后再试");
return ResultVo.success();
}
@ApiOperation("用户详情")
@GetMapping("/detail/{userName}")
public ResultVo detail(@PathVariable("userName") String userName) {
LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
List<SysUser> list = sysUserService.list(lambdaQueryWrapper.eq(SysUser::getUsername, userName));
AssertUtils.isTrue(list.size() > 0, "服务器繁忙请稍后再试");
return ResultVo.success(list.get(0));
}
@ApiOperation("登录管理系统")
@AnonymousAccess
@PostMapping("/login")
public ResultVo login(@RequestBody SysUser sysUser, HttpServletResponse response) {
LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
List<SysUser> list = sysUserService.list(lambdaQueryWrapper.eq(SysUser::getUsername, sysUser.getUsername()));
AssertUtils.isTrue(list.size() > 0, "用户名或密码错误");
SysUser thisUser=list.get(0);
String password = thisUser.getPassword().trim();
AssertUtils.isTrue(password.equals(sysUser.getPassword()), "用户名或密码错误");
UserInfoVo userToken = new UserInfoVo();
userToken.setUserId(thisUser.getUserId());
userToken.setUsername(thisUser.getUsername());
String token = ApiTokenUtils.createToken(userToken, BaseCacheKeys.APP_TOKEN_EXPIRE_MINUTES);
response.setHeader(properties.getHeader(), token);
String s= BaseCacheKeys.appTokenKey(token);
redisUtils.setObj(BaseCacheKeys.appTokenKey(token), userToken, BaseCacheKeys.APP_TOKEN_EXPIRE_MINUTES);
return ResultVo.success();
}
@ApiOperation("单点登录用户绑定业务系统用户")
@PostMapping("/blind")
public ResultVo blind(@RequestBody CasUserInfoVo vo) {
int blind = sysUserService.blind(vo);
AssertUtils.isTrue(blind > 0, "服务器繁忙请稍后再试");
return ResultVo.success();
}
@ApiOperation("单点登录用户管理列表")
@PostMapping("/manageList")
public ResultVo manageList(@RequestBody SysUser sysUser) {
SysUser thisUser=sysUserService.getById(sysUser.getUserId());
if(!thisUser.getIsAdmin()){
return ResultVo.fail("不是管理员");
}
LambdaQueryWrapper<SysUser> lambdaQueryWrapper=new LambdaQueryWrapper<>();
List<SysUser> userList=sysUserService.list(lambdaQueryWrapper.eq(SysUser::getEnabled,1L));
// AssertUtils.isTrue(blind > 0, "服务器繁忙请稍后再试");
return ResultVo.success(userList);
}
}
package com.zq.single.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "CasSystem对象", description = "")
public class CasSystem implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
@ApiModelProperty(value = "业务系统表名")
private String systemName;
@ApiModelProperty(value = "业务系统信息")
private String systemInfo;
}
package com.zq.single.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "CasUserInfo对象", description = "")
public class CasUserInfo implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
@ApiModelProperty(value = "单点登录用户id")
private Long userId;
@ApiModelProperty(value = "绑定系统id")
private Long systemId;
@ApiModelProperty(value = "绑定系统名")
private String systemName;
@ApiModelProperty(value = "绑定系统用户id")
private Long systemUserId;
@ApiModelProperty(value = "绑定系统用户名")
private String systemUserName;
}
package com.zq.single.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 系统用户
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "SysUser对象", description = "系统用户")
public class SysUser implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID")
@TableId(value = "user_id", type = IdType.AUTO)
private Long userId;
@ApiModelProperty(value = "部门名称")
private Long deptId;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "昵称")
private String nickName;
@ApiModelProperty(value = "性别")
private String gender;
@ApiModelProperty(value = "手机号码")
private String phone;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "头像地址")
private String avatarName;
@ApiModelProperty(value = "头像真实路径")
private String avatarPath;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "是否为admin账号")
private Boolean isAdmin;
@ApiModelProperty(value = "状态:1启用、0禁用")
private Long enabled;
@ApiModelProperty(value = "创建者")
private String createBy;
@ApiModelProperty(value = "更新着")
private String updateBy;
@ApiModelProperty(value = "修改密码的时间")
private LocalDateTime pwdResetTime;
@ApiModelProperty(value = "创建日期")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
}
package com.zq.single.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zq.single.entity.CasSystem;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* <p>
* Mapper 接口
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Mapper
@Repository
public interface CasSystemMapper extends BaseMapper<CasSystem> {
}
package com.zq.single.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zq.single.entity.CasUserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* <p>
* Mapper 接口
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Mapper
@Repository
public interface CasUserInfoMapper extends BaseMapper<CasUserInfo> {
}
package com.zq.single.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zq.single.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* <p>
* 系统用户 Mapper 接口
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Mapper
@Repository
public interface SysUserMapper extends BaseMapper<SysUser> {
}
package com.zq.single.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zq.single.entity.CasSystem;
/**
* <p>
* 服务类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
public interface ICasSystemService extends IService<CasSystem> {
}
package com.zq.single.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zq.single.entity.CasUserInfo;
import com.zq.single.vo.CasUserInfoVo;
/**
* <p>
* 服务类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
public interface ICasUserInfoService extends IService<CasUserInfo> {
int blind(CasUserInfoVo vo);
}
package com.zq.single.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zq.single.entity.SysUser;
import com.zq.single.vo.CasUserInfoVo;
/**
* <p>
* 系统用户 服务类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
public interface ISysUserService extends IService<SysUser> {
Boolean userNameIsExist(String userName);
int blind(CasUserInfoVo vo);
}
package com.zq.single.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zq.single.entity.CasSystem;
import com.zq.single.mapper.CasSystemMapper;
import com.zq.single.service.ICasSystemService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Service
public class CasSystemServiceImpl extends ServiceImpl<CasSystemMapper, CasSystem> implements ICasSystemService {
}
package com.zq.single.service.impl;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zq.common.utils.AssertUtils;
import com.zq.common.vo.ResultVo;
import com.zq.single.entity.CasUserInfo;
import com.zq.single.mapper.CasUserInfoMapper;
import com.zq.single.service.ICasUserInfoService;
import com.zq.single.vo.CasUserInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 服务实现类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Service
public class CasUserInfoServiceImpl extends ServiceImpl<CasUserInfoMapper, CasUserInfo> implements ICasUserInfoService {
@Autowired
private CasUserInfoMapper casUserInfoMapper;
RestTemplate restTemplate = new RestTemplate();
String sysServer = "http://127.0.0.1:9888/admin";
public int blind(CasUserInfoVo vo) {
Map<String, Object> params = new HashMap<>();
params.put("username", vo.getSystemUserName());
String s = SecureUtil.md5(vo.getSystemPassword());
System.out.println(s);
params.put("password", SecureUtil.md5(vo.getSystemPassword().trim()));
ResultVo resultVo = restTemplate.postForObject(sysServer + "/oauth/blindTest", params, ResultVo.class);
AssertUtils.isTrue(resultVo.isSuccess(), "绑定失败");
CasUserInfo info = new CasUserInfo();
info.setSystemId(vo.getSystemId());
info.setSystemId(vo.getSystemId());
info.setSystemUserId(vo.getSystemUserId());
info.setSystemUserName(vo.getSystemUserName());
return casUserInfoMapper.insert(info);
}
}
package com.zq.single.service.impl;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zq.common.utils.AssertUtils;
import com.zq.common.vo.ResultVo;
import com.zq.single.entity.CasUserInfo;
import com.zq.single.entity.SysUser;
import com.zq.single.mapper.CasUserInfoMapper;
import com.zq.single.mapper.SysUserMapper;
import com.zq.single.service.ISysUserService;
import com.zq.single.vo.CasUserInfoVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 系统用户 服务实现类
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@Service
@RequiredArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
private final SysUserMapper sysUserMapper;
private final CasUserInfoMapper casUserInfoMapper;
private RestTemplate restTemplate = new RestTemplate();
private String sysServer = "http://127.0.0.1:9888/admin";
@Override
public Boolean userNameIsExist(String userName) {
LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
Integer count = sysUserMapper.selectCount(lambdaQueryWrapper.eq(SysUser::getUsername, userName));
if (count > 0) {
return false;
} else {
return true;
}
}
@Override
public int blind(CasUserInfoVo vo) {
Map<String, Object> params = new HashMap<>();
params.put("username", vo.getSystemUserName());
String s = SecureUtil.md5(vo.getSystemPassword());
System.out.println(s);
params.put("password", SecureUtil.md5(vo.getSystemPassword().trim()));
ResultVo resultVo = restTemplate.postForObject(sysServer + "/oauth/blindTest", params, ResultVo.class);
AssertUtils.isTrue(resultVo.isSuccess(), "绑定失败");
CasUserInfo info = new CasUserInfo();
info.setSystemId(vo.getSystemId());
info.setSystemId(vo.getSystemId());
info.setSystemUserId(vo.getSystemUserId());
info.setSystemUserName(vo.getSystemUserName());
return casUserInfoMapper.insert(info);
}
}
package com.zq.single.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* <p>
*
* </p>
*
* @author Bill
* @since 2021-11-18
*/
@ApiModel(description = "绑定用户Vo")
@Data
public class CasUserInfoVo {
private Long id;
@ApiModelProperty(value = "单点登录用户id")
private Long userId;
@ApiModelProperty(value = "绑定系统id")
private Long systemId;
@ApiModelProperty(value = "绑定系统用户id")
private Long systemUserId;
@ApiModelProperty(value = "绑定系统用户名")
private String systemUserName;
@ApiModelProperty(value = "绑定系统用户密码")
private String systemPassword;
}
package com.zq.single.vo;
import com.zq.common.vo.ApiTokenVo;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author wilmiam
* @since 2021/11/22 11:46
*/
@Data
public class UserInfoVo extends ApiTokenVo {
private Boolean isAdmin;
private List<Map<String,String>> sonInfoList;
}
server:
port: 9878
#配置数据源
spring:
profiles:
active: @profiles.active@
application:
name: SINGLE-SERVER
servlet:
#上传文件限制
multipart:
#单个文件大小
max-file-size: 20MB
#设置总上传的数据大小
max-request-size: 50MB
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
redis:
#数据库索引
database: 0
host: 127.0.0.1
port: 6379
password:
#连接超时时间
timeout: 5000
datasource:
dynamic: # mybatis plus多数据源插件
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://47.107.148.253:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: Dk2019!23456
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
# 初始连接数
initial-size: 10
# 最小连接数
min-idle: 20
# 最大连接数
max-active: 50
# 获取连接超时时间
max-wait: 5000
# 连接有效性检测时间
time-between-eviction-runs-millis: 60000
# 连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
# 连接在池中最大生存的时间
max-evictable-idle-time-millis: 900000
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 检测连接是否有效
validation-query: SELECT 1 FROM DUAL
filters: stat
stat:
merge-sql: true
log-slow-sql: true
slow-sql-millis: 2000
# mybatis plus 配置
mybatis-plus:
configuration:
#打印sql,生产环境可已去掉
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
select-strategy: not_empty
update-strategy: not_empty
mapper-locations: classpath:/mapper/**.xml
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 7200000
# 在线用户key
online-key: online-token-
# 验证码
code-key: code-key-
# token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认 1小时,这里单位毫秒
renew: 3600000
\ No newline at end of file
......@@ -17,6 +17,7 @@ package com.zq.system.config.security;
import cn.hutool.core.util.StrUtil;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.context.ContextUtils;
import com.zq.common.vo.OnlineUserDto;
import com.zq.system.modules.system.service.OnlineUserService;
import com.zq.system.modules.system.service.UserCacheClean;
......@@ -85,6 +86,9 @@ public class TokenFilter extends GenericFilterBean {
SecurityContextHolder.getContext().setAuthentication(authentication);
// Token 续期
tokenProvider.checkRenewal(token);
// 设置当前用户
ContextUtils.setAdminContext(onlineUserDto);
}
}
filterChain.doFilter(servletRequest, servletResponse);
......
package com.zq.system.modules.system.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zq.system.modules.system.entity.BindUserInfo;
import org.springframework.stereotype.Repository;
/**
* 用户绑定系统信息表(TBlindUserInfo)表数据库访问层
*
* @author makejava
* @since 2021-11-26 16:18:10
*/
@Repository
public interface BlindUserInfoDao extends BaseMapper<BindUserInfo> {
}
\ No newline at end of file
package com.zq.system.modules.system.domain.vo;
import lombok.Data;
/**
* @author wilmiam
* @since 2021/11/26 16:37
*/
@Data
public class SingleLoginVo {
private Long systemId;
private String username;
private String passwd;
}
package com.zq.system.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* 用户绑定系统信息表(TBlindUserInfo)实体类
*
* @author makejava
* @since 2021-11-26 16:18:10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName(value = "t_blind_user_info")
public class BindUserInfo {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 单点登录用户id
*/
@ApiModelProperty("单点登录用户id")
private Long userId;
/**
* 绑定系统id
*/
@ApiModelProperty("绑定系统id")
private Long sysId;
/**
* 绑定系统名
*/
@ApiModelProperty("绑定系统名")
private String sysName;
/**
* 绑定系统用户id
*/
@ApiModelProperty("绑定系统用户id")
private String systemUserId;
/**
* 绑定系统用户名
*/
@ApiModelProperty("绑定系统用户名")
private String systemUserName;
/**
* 创建时间
*/
@ApiModelProperty("创建时间")
private Date createTime;
/**
* 更新时间
*/
@ApiModelProperty("更新时间")
private Date updateTime;
}
\ No newline at end of file
......@@ -36,6 +36,9 @@ public class SysInfo {
@ApiModelProperty("访问地址")
private String homeUrl;
@ApiModelProperty("登录地址")
private String loginUrl;
@ApiModelProperty("状态 0-禁用 1-启用")
private Integer state;
......
package com.zq.system.modules.system.rest;
import com.zq.common.annotation.rest.AnonymousGetMapping;
import com.zq.common.annotation.rest.AnonymousPostMapping;
import com.zq.common.utils.AssertUtils;
import com.zq.common.vo.IdVo;
import com.zq.common.vo.ResultVo;
import com.zq.system.modules.system.domain.User;
import com.zq.system.modules.system.domain.vo.SingleLoginVo;
import com.zq.system.modules.system.service.SingleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author wilmiam
* @since 2021/11/26 16:57
*/
@RestController
@RequiredArgsConstructor
@Api(tags = "单点登录")
@RequestMapping("/sys/single")
public class SingleController {
private final SingleService singleService;
@ApiOperation("单点登录")
@AnonymousPostMapping(value = "/login")
public void login(@RequestBody SingleLoginVo vo, HttpServletRequest request, HttpServletResponse response) {
AssertUtils.hasText(vo.getUsername(), "用户名为空");
AssertUtils.hasText(vo.getPasswd(), "密码为空");
singleService.login(vo, request, response);
}
@ApiOperation("获取用户信息")
@GetMapping(value = "/getUserInfo/{appId}")
public ResultVo getUserInfo(@PathVariable String appId) {
return ResultVo.success(singleService.getUserInfo(appId));
}
@ApiOperation("注册单点登录用户")
@AnonymousPostMapping(value = "/register")
public ResultVo register(@Validated(User.Create.class) @RequestBody User resources) {
singleService.register(resources);
return ResultVo.success("注册成功");
}
@ApiOperation("用户绑定系统")
@PostMapping(value = "/bindSystem")
public ResultVo bindSystem(@RequestBody SingleLoginVo vo) {
AssertUtils.notNull(vo.getSystemId(), "缺少系统ID");
AssertUtils.hasText(vo.getUsername(), "缺少用户名");
AssertUtils.hasText(vo.getPasswd(), "缺少密码");
singleService.bindSystem(vo);
return ResultVo.success();
}
@ApiOperation("用户绑定的系统列表")
@GetMapping(value = "/getBlindSysList")
public ResultVo getBlindSysList() {
return ResultVo.success(singleService.getBlindSysList());
}
@ApiOperation("取消绑定")
@PostMapping("/cancelBlind")
public ResultVo cancelBlind(@RequestBody IdVo vo) {
AssertUtils.notNull(vo.getIds());
singleService.cancelBlind(vo.getIds());
return ResultVo.success();
}
@ApiOperation("注册查用户名是否已存在")
@AnonymousGetMapping("/userNameIsExist/{userName}")
public ResultVo userNameIsExist(@PathVariable("userName") String userName) {
singleService.userNameIsExist(userName);
return ResultVo.success();
}
}
......@@ -16,22 +16,22 @@
package com.zq.system.modules.system.rest;
import cn.hutool.core.collection.CollectionUtil;
import com.zq.common.annotation.Log;
import com.zq.system.config.RsaProperties;
import com.zq.system.exception.BadRequestException;
import com.zq.system.modules.system.domain.User;
import com.zq.system.modules.system.domain.vo.UserPassVo;
import com.zq.system.modules.system.service.*;
import com.zq.system.modules.system.service.dto.RoleSmallDto;
import com.zq.system.modules.system.service.dto.UserDto;
import com.zq.system.modules.system.service.dto.UserQueryCriteria;
import com.zq.system.utils.PageUtil;
import com.zq.system.utils.RsaUtils;
import com.zq.system.utils.SecurityUtils;
import com.zq.system.utils.enums.CodeEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import com.zq.common.annotation.Log;
import com.zq.system.config.RsaProperties;
import com.zq.system.exception.BadRequestException;
import com.zq.system.modules.system.domain.vo.UserPassVo;
import com.zq.system.modules.system.service.dto.UserDto;
import com.zq.system.utils.enums.CodeEnum;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -66,6 +66,7 @@ public class UserController {
private final DeptService deptService;
private final RoleService roleService;
private final VerifyService verificationCodeService;
private final SingleService singleService;
@ApiOperation("导出用户数据")
@GetMapping(value = "/download")
......@@ -77,7 +78,7 @@ public class UserController {
@ApiOperation("查询用户")
@GetMapping
@PreAuthorize("@el.check('user:list')")
public ResponseEntity<Object> query(UserQueryCriteria criteria, Pageable pageable){
public ResponseEntity<Object> query(UserQueryCriteria criteria, Pageable pageable) {
if (!ObjectUtils.isEmpty(criteria.getDeptId())) {
criteria.getDeptIds().add(criteria.getDeptId());
criteria.getDeptIds().addAll(deptService.getDeptChildren(deptService.findByPid(criteria.getDeptId())));
......@@ -85,25 +86,25 @@ public class UserController {
// 数据权限
List<Long> dataScopes = dataService.getDeptIds(userService.findByName(SecurityUtils.getCurrentUsername()));
// criteria.getDeptIds() 不为空并且数据权限不为空则取交集
if (!CollectionUtils.isEmpty(criteria.getDeptIds()) && !CollectionUtils.isEmpty(dataScopes)){
if (!CollectionUtils.isEmpty(criteria.getDeptIds()) && !CollectionUtils.isEmpty(dataScopes)) {
// 取交集
criteria.getDeptIds().retainAll(dataScopes);
if(!CollectionUtil.isEmpty(criteria.getDeptIds())){
return new ResponseEntity<>(userService.queryAll(criteria,pageable), HttpStatus.OK);
if (!CollectionUtil.isEmpty(criteria.getDeptIds())) {
return new ResponseEntity<>(userService.queryAll(criteria, pageable), HttpStatus.OK);
}
} else {
// 否则取并集
criteria.getDeptIds().addAll(dataScopes);
return new ResponseEntity<>(userService.queryAll(criteria,pageable), HttpStatus.OK);
return new ResponseEntity<>(userService.queryAll(criteria, pageable), HttpStatus.OK);
}
return new ResponseEntity<>(PageUtil.toPage(null,0), HttpStatus.OK);
return new ResponseEntity<>(PageUtil.toPage(null, 0), HttpStatus.OK);
}
@Log("新增用户")
@ApiOperation("新增用户")
@PostMapping
@PreAuthorize("@el.check('user:add')")
public ResponseEntity<Object> create(@Validated @RequestBody User resources){
public ResponseEntity<Object> create(@Validated @RequestBody User resources) {
checkLevel(resources);
// 默认密码 123456
resources.setPassword(passwordEncoder.encode("gxmz!23"));
......@@ -124,8 +125,8 @@ public class UserController {
@Log("修改用户:个人中心")
@ApiOperation("修改用户:个人中心")
@PutMapping(value = "center")
public ResponseEntity<Object> center(@Validated(User.Update.class) @RequestBody User resources){
if(!resources.getId().equals(SecurityUtils.getCurrentUserId())){
public ResponseEntity<Object> center(@Validated(User.Update.class) @RequestBody User resources) {
if (!resources.getId().equals(SecurityUtils.getCurrentUserId())) {
throw new BadRequestException("不能修改他人资料");
}
userService.updateCenter(resources);
......@@ -136,10 +137,10 @@ public class UserController {
@ApiOperation("删除用户")
@DeleteMapping
@PreAuthorize("@el.check('user:del')")
public ResponseEntity<Object> delete(@RequestBody Set<Long> ids){
public ResponseEntity<Object> delete(@RequestBody Set<Long> ids) {
for (Long id : ids) {
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
if (currentLevel > optLevel) {
throw new BadRequestException("角色权限不足,不能删除:" + userService.findById(id).getUsername());
}
......@@ -151,22 +152,22 @@ public class UserController {
@ApiOperation("修改密码")
@PostMapping(value = "/updatePass")
public ResponseEntity<Object> updatePass(@RequestBody UserPassVo passVo) throws Exception {
String oldPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getOldPass());
String newPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,passVo.getNewPass());
String oldPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, passVo.getOldPass());
String newPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, passVo.getNewPass());
UserDto user = userService.findByName(SecurityUtils.getCurrentUsername());
if(!passwordEncoder.matches(oldPass, user.getPassword())){
if (!passwordEncoder.matches(oldPass, user.getPassword())) {
throw new BadRequestException("修改失败,旧密码错误");
}
if(passwordEncoder.matches(newPass, user.getPassword())){
if (passwordEncoder.matches(newPass, user.getPassword())) {
throw new BadRequestException("新密码不能与旧密码相同");
}
userService.updatePass(user.getUsername(),passwordEncoder.encode(newPass));
userService.updatePass(user.getUsername(), passwordEncoder.encode(newPass));
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiOperation("修改头像")
@PostMapping(value = "/updateAvatar")
public ResponseEntity<Object> updateAvatar(@RequestParam MultipartFile avatar){
public ResponseEntity<Object> updateAvatar(@RequestParam MultipartFile avatar) {
return new ResponseEntity<>(userService.updateAvatar(avatar), HttpStatus.OK);
}
......@@ -174,22 +175,23 @@ public class UserController {
@ApiOperation("修改邮箱")
@PostMapping(value = "/updateEmail/{code}")
public ResponseEntity<Object> updateEmail(@PathVariable String code, @RequestBody User user) throws Exception {
String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey,user.getPassword());
String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, user.getPassword());
UserDto userDto = userService.findByName(SecurityUtils.getCurrentUsername());
if(!passwordEncoder.matches(password, userDto.getPassword())){
if (!passwordEncoder.matches(password, userDto.getPassword())) {
throw new BadRequestException("密码错误");
}
verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + user.getEmail(), code);
userService.updateEmail(userDto.getUsername(),user.getEmail());
userService.updateEmail(userDto.getUsername(), user.getEmail());
return new ResponseEntity<>(HttpStatus.OK);
}
/**
* 如果当前用户的角色级别低于创建用户的角色级别,则抛出权限不足的错误
*
* @param resources /
*/
private void checkLevel(User resources) {
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer currentLevel = Collections.min(roleService.findByUsersId(SecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
Integer optLevel = roleService.findByRoles(resources.getRoles());
if (currentLevel > optLevel) {
throw new BadRequestException("角色权限不足");
......
package com.zq.system.modules.system.service;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zq.common.config.redis.RedisUtils;
import com.zq.common.config.security.SecurityProperties;
import com.zq.common.context.ContextUtils;
import com.zq.common.utils.AssertUtils;
import com.zq.common.vo.OnlineUserDto;
import com.zq.common.vo.ResultVo;
import com.zq.system.config.RsaProperties;
import com.zq.system.config.security.TokenProvider;
import com.zq.system.modules.system.dao.BlindUserInfoDao;
import com.zq.system.modules.system.dao.SysInfoDao;
import com.zq.system.modules.system.domain.Job;
import com.zq.system.modules.system.domain.Role;
import com.zq.system.modules.system.domain.User;
import com.zq.system.modules.system.domain.vo.SingleLoginVo;
import com.zq.system.modules.system.entity.BindUserInfo;
import com.zq.system.modules.system.entity.SysInfo;
import com.zq.system.modules.system.repository.DeptRepository;
import com.zq.system.modules.system.repository.JobRepository;
import com.zq.system.modules.system.repository.RoleRepository;
import com.zq.system.modules.system.repository.UserRepository;
import com.zq.system.utils.RsaUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author wilmiam
* @since 2021/11/26 16:20
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SingleService {
private final UserRepository userRepository;
private final BlindUserInfoDao blindUserInfoDao;
private final PasswordEncoder passwordEncoder;
private final DeptRepository deptRepository;
private final RoleRepository roleRepository;
private final JobRepository jobRepository;
private final UserService userService;
private final SysInfoDao sysInfoDao;
private final SecurityProperties properties;
private final OnlineUserService onlineUserService;
private final RedisUtils redisUtils;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
public void register(User resources) {
String passwd = null;
try {
passwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, resources.getPassword());
} catch (Exception e) {
e.printStackTrace();
}
AssertUtils.hasText(passwd, "密码解密失败");
resources.setPassword(passwordEncoder.encode(passwd));
List<Role> roleList = roleRepository.findAllById(Collections.singletonList(2L));
resources.setRoles(new HashSet<>(roleList));
List<Job> jobList = jobRepository.findAllById(Collections.singletonList(13L));
resources.setJobs(new HashSet<>(jobList));
deptRepository.findById(8L).ifPresent(resources::setDept);
userService.create(resources);
}
public List<BindUserInfo> getBlindSysList() {
Long adminUserId = ContextUtils.getAdminUserId();
AssertUtils.notNull(adminUserId, "登录信息已过期");
return blindUserInfoDao.selectList(Wrappers.lambdaQuery(BindUserInfo.builder().userId(adminUserId).build()));
}
public void cancelBlind(Set<String> ids) {
blindUserInfoDao.deleteBatchIds(ids);
}
public void userNameIsExist(String userName) {
User user = userRepository.findByUsername(userName);
AssertUtils.isTrue(user == null, "用户名已存在");
}
public void bindSystem(SingleLoginVo vo) {
SysInfo sysInfo = sysInfoDao.selectById(vo.getSystemId());
AssertUtils.notNull(sysInfo, "系统不存在");
AssertUtils.hasText(sysInfo.getLoginUrl(), "系统缺少验证配置");
HttpRequest request = HttpRequest.post(sysInfo.getLoginUrl())
.form("username", vo.getUsername())
.form("password", vo.getPasswd());
HttpResponse response = request.execute();
String body = response.body();
if (response.getStatus() != 200) {
log.error("{}系统登录地址请求失败:{}", sysInfo.getSystemName(), body);
}
ResultVo resultVo = JSONUtil.toBean(body, ResultVo.class);
if (resultVo.isSuccess()) {
JSONObject obj = JSONUtil.parseObj(resultVo.getData());
String userId = obj.getStr("userId");
String username = obj.getStr("username");
BindUserInfo build = BindUserInfo.builder()
.userId(ContextUtils.getAdminUserId())
.sysId(sysInfo.getId())
.sysName(sysInfo.getSystemName())
.systemUserId(userId)
.systemUserName(username)
.build();
blindUserInfoDao.insert(build);
}
}
public void login(SingleLoginVo vo, HttpServletRequest request, HttpServletResponse response) {
try {
String serviceUrl = request.getParameter("service");
AssertUtils.hasText(serviceUrl, "跳转地址为空");
String passwd = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, vo.getPasswd());
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(vo.getUsername(), passwd);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌
String token = TokenProvider.createToken(authentication);
final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
// 保存在线信息
onlineUserService.save(jwtUserDto, token, request);
String key = IdUtil.simpleUUID();
redisUtils.setStr(token, key, properties.getTokenValidityInSeconds() / 1000, TimeUnit.SECONDS);
response.setHeader(properties.getHeader(), properties.getTokenStartWith() + token);
response.sendRedirect(serviceUrl + "?" + properties.getHeader() + "=" + properties.getTokenStartWith() + token + "&key=" + key);
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getUserInfo(String appId) {
OnlineUserDto adminContext = ContextUtils.getAdminContext();
SysInfo sysInfo = sysInfoDao.selectOne(Wrappers.lambdaQuery(SysInfo.builder().appId(appId).build()));
BindUserInfo bindUserInfo = blindUserInfoDao.selectOne(Wrappers.lambdaQuery(BindUserInfo.builder().sysId(sysInfo.getId()).userId(adminContext.getUserId()).build()));
AssertUtils.notNull(bindUserInfo, 501, "用户未绑定系统");
Map<String, Object> data = new HashMap<>();
data.put("userId", bindUserInfo.getSystemUserId());
data.put("username", bindUserInfo.getSystemUserName());
return data;
}
}
......@@ -165,5 +165,4 @@ public interface UserService {
* @return
*/
ApiTokenVo passwdLogin(LoginVo vo);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment