init
This commit is contained in:
75
auth/pom.xml
Normal file
75
auth/pom.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<?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>fateverse</artifactId>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
</properties>
|
||||
|
||||
<artifactId>auth</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-swagger</artifactId>
|
||||
</dependency>
|
||||
<!--通用模块-->
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-log</artifactId>
|
||||
</dependency>
|
||||
<!-- 验证码 -->
|
||||
<dependency>
|
||||
<groupId>com.github.penggle</groupId>
|
||||
<artifactId>kaptcha</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.fateverse</groupId>
|
||||
<artifactId>common-file</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.7.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
20
auth/src/main/java/cn/fateverse/auth/AuthApplication.java
Normal file
20
auth/src/main/java/cn/fateverse/auth/AuthApplication.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package cn.fateverse.auth;
|
||||
|
||||
import cn.fateverse.common.security.annotation.EnableSecurity;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
@EnableSecurity
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class AuthApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthApplication.class,args);
|
||||
System.out.println("授权中心启动成功");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.auth.config;
|
||||
|
||||
import cn.fateverse.auth.event.LoginInfoListener;
|
||||
import cn.fateverse.auth.service.UserDetailsServiceImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2023-11-10 20:16
|
||||
*/
|
||||
@Configuration
|
||||
public class AuthConfiguration {
|
||||
|
||||
|
||||
@Resource
|
||||
private UserDetailsServiceImpl userDetailsService;
|
||||
|
||||
@Resource
|
||||
private BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
@Bean
|
||||
public LoginInfoListener loginInfoListener(ThreadPoolTaskExecutor taskExecuteExecutor){
|
||||
return new LoginInfoListener(taskExecuteExecutor);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
|
||||
return httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
|
||||
.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(bCryptPasswordEncoder)
|
||||
.and()
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.fateverse.auth.config;
|
||||
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.code.kaptcha.Constants.*;
|
||||
|
||||
/**
|
||||
* 验证码配置
|
||||
*
|
||||
* @author Clay
|
||||
*/
|
||||
@Configuration
|
||||
public class CaptchaConfig {
|
||||
@Bean
|
||||
public DefaultKaptcha kaptchaProducer() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(KAPTCHA_BORDER,"no");
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH,"200");
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT,"60");
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE,"40");
|
||||
// properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR,"0,0,0"); //字体颜色(RGB)
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
|
||||
properties.setProperty(KAPTCHA_BACKGROUND_CLR_FROM,"253,220,114");
|
||||
properties.setProperty(KAPTCHA_BACKGROUND_CLR_TO,"65,88,208");
|
||||
properties.setProperty(KAPTCHA_BACKGROUND_IMPL,"com.google.code.kaptcha.impl.DefaultBackground");
|
||||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
// 图片样式
|
||||
//水纹 com.google.code.kaptcha.impl.WaterRipple
|
||||
//鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy
|
||||
//阴影 com.google.code.kaptcha.impl.ShadowGimpy
|
||||
// properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_STRING,"123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#$!@&%");
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_STRING,"123456789#$@&%");
|
||||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package cn.fateverse.auth.controller;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.fateverse.common.core.constant.CacheConstants;
|
||||
import cn.fateverse.common.core.constant.Constants;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import cn.fateverse.common.core.utils.uuid.IdUtils;
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
@Api(tags = "验证码接口")
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class CaptchaController {
|
||||
|
||||
@Resource
|
||||
private Producer producer;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
@ApiOperation("生成验证码")
|
||||
@GetMapping("/captchaImage")
|
||||
public Result<Map<String, Object>> getCode() {
|
||||
// 保存验证码信息
|
||||
String uuid = IdUtils.simpleUUID();
|
||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
|
||||
String code = producer.createText();
|
||||
BufferedImage image = producer.createImage(code);
|
||||
redisTemplate.opsForValue().set(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
|
||||
// 将图片转换为base64
|
||||
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(image, "jpg", os);
|
||||
} catch (IOException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>(0);
|
||||
result.put("uuid", uuid);
|
||||
result.put("img", Base64.encode(os.toByteArray()));
|
||||
return Result.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cn.fateverse.auth.controller;
|
||||
|
||||
import cn.fateverse.admin.vo.RouterVo;
|
||||
import cn.fateverse.auth.entity.UserInfo;
|
||||
import cn.fateverse.auth.entity.LoginBody;
|
||||
import cn.fateverse.auth.service.LoginService;
|
||||
import cn.fateverse.common.core.result.Result;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
@Api(tags = "登录")
|
||||
@RestController
|
||||
public class LoginController {
|
||||
|
||||
private final LoginService loginService;
|
||||
|
||||
public LoginController(LoginService loginService) {
|
||||
this.loginService = loginService;
|
||||
}
|
||||
|
||||
@ApiOperation("登录")
|
||||
@PostMapping("/login")
|
||||
public Result<String> login(@Validated @RequestBody LoginBody login) {
|
||||
return Result.ok("登录成功",loginService.login(login));
|
||||
}
|
||||
|
||||
@ApiOperation("登出")
|
||||
@PostMapping("/logout")
|
||||
public Result<String> logout(){
|
||||
loginService.logout();
|
||||
return Result.ok("登出成功");
|
||||
}
|
||||
|
||||
@ApiOperation("获取用户信息")
|
||||
@GetMapping("/info")
|
||||
public Result<UserInfo> info() {
|
||||
return Result.ok(loginService.getInfo());
|
||||
}
|
||||
|
||||
@ApiOperation("获取用户菜单信息")
|
||||
@GetMapping("/router")
|
||||
public Result<List<RouterVo>> router() {
|
||||
return Result.ok(loginService.getMenuRouterByUserId());
|
||||
}
|
||||
|
||||
// todo 王阳蓝
|
||||
//phone
|
||||
//mail 邮件
|
||||
//sms 短信 阿里大鱼
|
||||
//qq
|
||||
//wechat
|
||||
//wechat 小程序
|
||||
}
|
||||
42
auth/src/main/java/cn/fateverse/auth/entity/LoginBody.java
Normal file
42
auth/src/main/java/cn/fateverse/auth/entity/LoginBody.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package cn.fateverse.auth.entity;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("登录数据实体")
|
||||
public class LoginBody {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@ApiModelProperty("用户名")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
@ApiModelProperty("用户密码")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
/**
|
||||
* 验证码uuid
|
||||
*/
|
||||
@ApiModelProperty("验证码uuid")
|
||||
@NotBlank(message = "验证码uuid")
|
||||
private String uuid;
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
@ApiModelProperty("验证码")
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
}
|
||||
38
auth/src/main/java/cn/fateverse/auth/entity/UserInfo.java
Normal file
38
auth/src/main/java/cn/fateverse/auth/entity/UserInfo.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package cn.fateverse.auth.entity;
|
||||
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/3
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class UserInfo {
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
private Set<String> roles;
|
||||
|
||||
/**
|
||||
* 权限列表
|
||||
*/
|
||||
private Set<String> permissions;
|
||||
|
||||
|
||||
|
||||
}
|
||||
30
auth/src/main/java/cn/fateverse/auth/enums/LoginStatus.java
Normal file
30
auth/src/main/java/cn/fateverse/auth/enums/LoginStatus.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package cn.fateverse.auth.enums;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/2
|
||||
*/
|
||||
public enum LoginStatus {
|
||||
/**
|
||||
* 登录状态
|
||||
*/
|
||||
SUCCESS(0, "登录成功!"),
|
||||
FAIL(1, "登录失败");
|
||||
|
||||
private final Integer code;
|
||||
private final String info;
|
||||
|
||||
LoginStatus(Integer code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.fateverse.auth.event;
|
||||
|
||||
import cn.fateverse.log.entity.LoginInfo;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 登录操作的日志收集,采用时间发布机制触发日志保存的操作
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/11/2
|
||||
*/
|
||||
public class LoginInfoEvent extends ApplicationEvent {
|
||||
|
||||
|
||||
public LoginInfoEvent(LoginInfo source) {
|
||||
super(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.fateverse.auth.event;
|
||||
|
||||
import cn.fateverse.log.dubbo.DubboLogService;
|
||||
import cn.fateverse.log.entity.LoginInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/2
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoginInfoListener {
|
||||
|
||||
@DubboReference
|
||||
private DubboLogService logService;
|
||||
|
||||
private final ThreadPoolTaskExecutor executor;
|
||||
|
||||
public LoginInfoListener(ThreadPoolTaskExecutor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoginInfoListener监听器,监听LoginInfoEvent所发布的时间,调用rpc实现远程日志记录
|
||||
* @param event
|
||||
*/
|
||||
@EventListener(LoginInfoEvent.class)
|
||||
public void saveLoginInfo(LoginInfoEvent event){
|
||||
executor.execute(()->{
|
||||
LoginInfo loginInfo = (LoginInfo) event.getSource();
|
||||
log.info(loginInfo.toString());
|
||||
logService.saveLoginInfo(loginInfo);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.fateverse.auth.service;
|
||||
|
||||
import cn.fateverse.admin.vo.RouterVo;
|
||||
import cn.fateverse.auth.entity.UserInfo;
|
||||
import cn.fateverse.auth.entity.LoginBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
public interface LoginService {
|
||||
|
||||
/**
|
||||
* 登录获取token验证码
|
||||
*
|
||||
* @param login
|
||||
* @return
|
||||
*/
|
||||
String login(LoginBody login);
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*/
|
||||
void logout();
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @return
|
||||
*/
|
||||
UserInfo getInfo();
|
||||
|
||||
/**
|
||||
* 获取用户的路由信息
|
||||
* @return
|
||||
*/
|
||||
List<RouterVo> getMenuRouterByUserId();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.fateverse.auth.service;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboUserService;
|
||||
import cn.fateverse.common.core.enums.UserState;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.utils.ObjectUtils;
|
||||
import cn.fateverse.common.security.entity.LoginUser;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@DubboReference
|
||||
private DubboUserService userService;
|
||||
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
//todo 编辑用户登录相关逻辑
|
||||
log.info("有用户登录:{}",username);
|
||||
User user = userService.getUserByUsername(username);
|
||||
if (ObjectUtils.isEmpty(user)) {
|
||||
log.info("登录用户:{} 不存在.", username);
|
||||
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||
} else if (UserState.DELETED.getCode().equals(user.getDelFlag())) {
|
||||
log.info("登录用户:{} 已被删除.", username);
|
||||
throw new CustomException("对不起,您的账号:" + username + " 已被删除");
|
||||
} else if (UserState.DISABLE.getCode().equals(user.getState())) {
|
||||
log.info("登录用户:{} 已被停用.", username);
|
||||
throw new CustomException("对不起,您的账号:" + username + " 已停用");
|
||||
}
|
||||
return new LoginUser(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package cn.fateverse.auth.service.impl;
|
||||
|
||||
import cn.fateverse.auth.entity.UserInfo;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.fateverse.admin.dubbo.DubboMenuService;
|
||||
import cn.fateverse.admin.dubbo.DubboUserService;
|
||||
import cn.fateverse.admin.vo.RouterVo;
|
||||
import cn.fateverse.auth.entity.LoginBody;
|
||||
import cn.fateverse.auth.service.LoginService;
|
||||
import cn.fateverse.admin.entity.Role;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.common.core.constant.CacheConstants;
|
||||
import cn.fateverse.common.core.constant.DateConstants;
|
||||
import cn.fateverse.common.core.exception.CustomException;
|
||||
import cn.fateverse.common.core.exception.UserPasswordNotMatchException;
|
||||
import cn.fateverse.common.core.utils.SpringContextHolder;
|
||||
import cn.fateverse.common.core.utils.uuid.IdUtils;
|
||||
import cn.fateverse.common.security.entity.LoginUser;
|
||||
import cn.fateverse.common.security.service.TokenService;
|
||||
import cn.fateverse.common.security.utils.SecurityUtils;
|
||||
import cn.fateverse.auth.event.LoginInfoEvent;
|
||||
import cn.fateverse.auth.utils.LoginInfoUtil;
|
||||
import cn.fateverse.log.entity.LoginInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 登录service服务
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/27
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LoginServiceImpl implements LoginService {
|
||||
|
||||
|
||||
@Resource
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Resource
|
||||
private TokenService tokenService;
|
||||
|
||||
private final PermissionService permissionService;
|
||||
|
||||
/**
|
||||
* 临时处理
|
||||
*/
|
||||
@DubboReference
|
||||
private DubboUserService userService;
|
||||
|
||||
@DubboReference
|
||||
private DubboMenuService menuService;
|
||||
|
||||
@Resource
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Resource
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
|
||||
public LoginServiceImpl(PermissionService permissionService) {
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String login(LoginBody login) {
|
||||
log.info("用户:{},于:{}登录系统", login.getUsername(), DateUtil.format(new Date(), DateConstants.YYYY_MM_DD_HH_MM_SS));
|
||||
String uuid = CacheConstants.CAPTCHA_CODE_KEY + login.getUuid();
|
||||
String code = String.valueOf(redisTemplate.opsForValue().get(uuid));
|
||||
if (null == code) {
|
||||
publishEvent(login.getUsername(), "验证码已过期!", Boolean.FALSE, null);
|
||||
throw new CustomException("验证码已过期!");
|
||||
}
|
||||
if (!code.equals(login.getCode())) {
|
||||
publishEvent(login.getUsername(), "验证码错误!", Boolean.FALSE, null);
|
||||
throw new CustomException("验证码错误!");
|
||||
}
|
||||
redisTemplate.delete(uuid);
|
||||
//用户验证
|
||||
Authentication authentication = null;
|
||||
try {
|
||||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
||||
authentication = authenticationManager
|
||||
.authenticate(new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()));
|
||||
} catch (Exception e) {
|
||||
if (e instanceof BadCredentialsException) {
|
||||
throw new UserPasswordNotMatchException();
|
||||
} else {
|
||||
publishEvent(login.getUsername(), "登录失败,请联系管理员", Boolean.FALSE, null);
|
||||
throw new CustomException(e.getMessage());
|
||||
}
|
||||
}
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
permissionService.getMenuPermission(loginUser);
|
||||
loginUser.getUser().setPassword(null);
|
||||
String userUUId = IdUtils.fastUUID();
|
||||
loginUser.setUuid(userUUId);
|
||||
LoginInfo loginInfo = publishEvent(login.getUsername(), null, Boolean.TRUE, userUUId);
|
||||
setLoginInfo(loginUser, loginInfo);
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout() {
|
||||
tokenService.userLogout();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置登录的信息,比如操作系统浏览器等
|
||||
*
|
||||
* @param user
|
||||
* @param info
|
||||
*/
|
||||
private void setLoginInfo(LoginUser user, LoginInfo info) {
|
||||
user.setLoginLocation(info.getLoginLocation());
|
||||
user.setLoginTime(System.currentTimeMillis());
|
||||
user.setIpddr(info.getIpddr());
|
||||
user.setBrowser(info.getBrowser());
|
||||
user.setOs(info.getOs());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserInfo getInfo() {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
User user = loginUser.getUser();
|
||||
Set<String> roleNameSet = user.getRoles().stream().map(Role::getRoleKey).collect(Collectors.toSet());
|
||||
user.setPassword(null);
|
||||
return UserInfo.builder()
|
||||
.user(user)
|
||||
.permissions(loginUser.getPermissions())
|
||||
.roles(roleNameSet)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<RouterVo> getMenuRouterByUserId() {
|
||||
List<RouterVo> result = (List<RouterVo>) redisTemplate.opsForValue().get(CacheConstants.ROUTE_CACHE_KEY + SecurityUtils.getUserId());
|
||||
if (result == null || result.isEmpty()) {
|
||||
RLock lock = redissonClient.getLock(CacheConstants.ROUTE_CACHE_KEY + "lock:" + SecurityUtils.getUserId());
|
||||
try {
|
||||
result = (List<RouterVo>) redisTemplate.opsForValue().get(CacheConstants.ROUTE_CACHE_KEY + SecurityUtils.getUserId());
|
||||
if (result == null || result.isEmpty()) {
|
||||
result = menuService.selectMenuRouterByUserId(SecurityUtils.getUserId());
|
||||
if (result == null || result.isEmpty()) {
|
||||
log.info("用户:[{}],用户id:[{}],获取路由异常!", SecurityUtils.getUsername(), SecurityUtils.getUserId());
|
||||
// throw new CustomException("获取路由异常!");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
redisTemplate.opsForValue().set(CacheConstants.ROUTE_CACHE_KEY + SecurityUtils.getUserId(),result,30, TimeUnit.MINUTES);
|
||||
}
|
||||
} finally {
|
||||
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的消息发布函数
|
||||
*
|
||||
* @param username
|
||||
* @param msg
|
||||
* @param start
|
||||
*/
|
||||
private LoginInfo publishEvent(String username, String msg, Boolean start, String uuid) {
|
||||
LoginInfo loginInfo = null;
|
||||
if (start) {
|
||||
loginInfo = LoginInfoUtil.successLogin(username);
|
||||
loginInfo.setUuid(uuid);
|
||||
} else {
|
||||
loginInfo = LoginInfoUtil.successFail(username, msg);
|
||||
}
|
||||
SpringContextHolder.publishEvent(new LoginInfoEvent(loginInfo));
|
||||
return loginInfo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.fateverse.auth.service.impl;
|
||||
|
||||
import cn.fateverse.admin.dubbo.DubboMenuService;
|
||||
import cn.fateverse.admin.entity.User;
|
||||
import cn.fateverse.common.security.entity.LoginUser;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 用户权限处理
|
||||
*
|
||||
* @author Clay
|
||||
* @date 2022/10/30
|
||||
*/
|
||||
@Component
|
||||
public class PermissionService {
|
||||
|
||||
@DubboReference
|
||||
private DubboMenuService menuService;
|
||||
|
||||
/**
|
||||
* 获取到菜单权限信息
|
||||
*
|
||||
* @param loginUser 登录用户
|
||||
*/
|
||||
public void getMenuPermission(LoginUser loginUser) {
|
||||
User user = loginUser.getUser();
|
||||
Set<String> perms = new HashSet<>();
|
||||
if (user.isAdmin()) {
|
||||
perms.add("*:*:*");
|
||||
} else {
|
||||
Set<String> menuSet = menuService.selectMenuPermsByUserId(user.getUserId());
|
||||
perms.addAll(menuSet);
|
||||
}
|
||||
loginUser.setPermissions(perms);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package cn.fateverse.auth.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.fateverse.common.core.utils.HttpServletUtils;
|
||||
import cn.fateverse.common.core.utils.IpUtils;
|
||||
import cn.fateverse.auth.enums.LoginStatus;
|
||||
import cn.fateverse.log.entity.LoginInfo;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Clay
|
||||
* @date 2022/11/2
|
||||
*/
|
||||
public class LoginInfoUtil {
|
||||
|
||||
/**
|
||||
* 成功是的登录信息实体
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
public static LoginInfo successLogin(String userName){
|
||||
LoginInfo loginInfo = new LoginInfo();
|
||||
loginInfo.setUserName(userName);
|
||||
loginInfo.setState(LoginStatus.SUCCESS.getCode());
|
||||
loginInfo.setMsg(LoginStatus.SUCCESS.getInfo());
|
||||
setSystemInfo(loginInfo);
|
||||
return loginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败时的登录信息实体
|
||||
*
|
||||
* @param userName
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static LoginInfo successFail(String userName,String msg){
|
||||
LoginInfo loginInfo = new LoginInfo();
|
||||
loginInfo.setUserName(userName);
|
||||
loginInfo.setState(LoginStatus.FAIL.getCode());
|
||||
if (!StrUtil.isEmpty(msg)){
|
||||
loginInfo.setMsg(msg);
|
||||
}else {
|
||||
loginInfo.setMsg(LoginStatus.FAIL.getInfo());
|
||||
}
|
||||
setSystemInfo(loginInfo);
|
||||
return loginInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置系统相关信息
|
||||
* @param loginInfo
|
||||
*/
|
||||
private static void setSystemInfo(LoginInfo loginInfo){
|
||||
HttpServletRequest request = HttpServletUtils.getRequest();
|
||||
String ipAdder = IpUtils.getIpAdder(request);
|
||||
loginInfo.setIpddr(ipAdder);
|
||||
final UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
|
||||
//获取操作系统信息
|
||||
String osName = userAgent.getOperatingSystem().getName();
|
||||
loginInfo.setOs(osName);
|
||||
//获取浏览器信息
|
||||
String browser = userAgent.getBrowser().getName();
|
||||
loginInfo.setBrowser(browser);
|
||||
loginInfo.setLoginTime(new Date());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
31
auth/src/main/resources/bootstrap-dev.yml
Normal file
31
auth/src/main/resources/bootstrap-dev.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
## Spring
|
||||
#spring:
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# # 服务注册地址
|
||||
# server-addr: 10.7.127.185:38848
|
||||
# namespace: clay
|
||||
#
|
||||
#dubbo:
|
||||
# registry:
|
||||
# parameters:
|
||||
# namespace: dubbo-clay
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
# server-addr: 10.7.127.185:38848
|
||||
server-addr: 162.14.111.170:8848
|
||||
|
||||
namespace: gary
|
||||
|
||||
|
||||
|
||||
dubbo:
|
||||
registry:
|
||||
parameters:
|
||||
namespace: dubbo-gary
|
||||
12
auth/src/main/resources/bootstrap-pro.yml
Normal file
12
auth/src/main/resources/bootstrap-pro.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
# Spring
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: nacos.fateverse.svc.cluster.local:8848
|
||||
|
||||
|
||||
management:
|
||||
server:
|
||||
port: 9595
|
||||
47
auth/src/main/resources/bootstrap.yml
Normal file
47
auth/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
# Tomcat
|
||||
server:
|
||||
port: 9008
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: auth
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
mvc:
|
||||
pathmatch:
|
||||
matching-strategy: ant_path_matcher
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 192.168.101.108:8848
|
||||
username: nacos
|
||||
password: nacos
|
||||
namespace: ${spring.profiles.active}
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||
file-extension: yaml
|
||||
namespace: ${spring.profiles.active}
|
||||
shared-configs:
|
||||
- data-id: application-${spring.profiles.active}.yaml
|
||||
refresh: true
|
||||
|
||||
|
||||
dubbo:
|
||||
application:
|
||||
name: dubbo-${spring.application.name}
|
||||
protocol:
|
||||
name: dubbo
|
||||
port: -1
|
||||
registry:
|
||||
address: nacos://${spring.cloud.nacos.discovery.server-addr}
|
||||
username: ${spring.cloud.nacos.discovery.username}
|
||||
password: ${spring.cloud.nacos.discovery.password}
|
||||
parameters:
|
||||
namespace: dubbo-${spring.profiles.active}
|
||||
Reference in New Issue
Block a user