5.2 登录成功:生成token
- 准备工作已完成
- 登录成功,将 EduUser 转换成token,并响应给用户
@PostMapping("/login") public BaseResult login(@RequestBody EduUser eduUser) { //1 校验验证码 // 1.1 获得redis String redisName = "login_verify_code_" + eduUser.getUsername(); String redisVerifyCode = stringRedisTemplate.opsForValue().get(redisName); // 1.2 删除redis stringRedisTemplate.delete(redisName); // 1.3 校验:无效 if(redisVerifyCode == null) { return BaseResult.error("验证码无效"); } // 1.4 校验:错误 if(!redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) { return BaseResult.error("验证码错误"); } //2 通过service用户登录 EduUser loginUser = eduUserService.login(eduUser); //3 提示 if(loginUser != null) { if("0".equals(loginUser.getStatus())) { return BaseResult.error("用户未激活,请先激活"); } if("2".equals(loginUser.getStatus())) { return BaseResult.error("临时冻结,36小时"); } if("3".equals(loginUser.getStatus())) { return BaseResult.error("该账号已冻结"); } // 需要设置token //String token = "admin-token"; String token = JwtUtils.generateToken(loginUser, jwtProperties.getExpire(), jwtProperties.getPrivateKey()); return BaseResult.ok("登录成功").append("token", token); } return BaseResult.error("用户名或密码不匹配"); }
5.3 查询详情:获得token
5.3.1 基本流程
- 登录成功后,默认跳转到 / 页面
访问 / ,在路由中配置跳转的位置
- 在跳转 / 页面前,执行vuex中
user/getInfo
通过vuex执行ajax请求,查询详情
5.3.2 查询详情
- 修改 EduUserController 添加方法
@GetMapping("/info") public BaseResult info( String token) { try { //1 通过token 获得用户信息 EduUser eduUser = JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class); //2 模拟数据 /* Map<String,Object> map = new HashMap<>(); if("admin-token".equalsIgnoreCase(token)) { map.put("roles", Arrays.asList("admin")); //角色的值必须是数组 map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); map.put("name","张三三"); } else { // 非管理员的权限 }*/ //3 真实数据 Map<String,Object> map = new HashMap<>(); if(eduUser.getRoles() != null) { map.put("roles", eduUser.getRoles().split(",")); //角色的值必须是数组 } else { map.put("roles", Arrays.asList("editor")); //没有权限的固定:editor } map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); //需要完善用户头像 map.put("name",eduUser.getUsername()); // 数据返回 baseResult.data --> Map return BaseResult.ok("获得权限成功", map); } catch (Exception e) { return BaseResult.error("获得权限失败"); } }
5.4 过滤器
5.4.1 配置yml
sc: jwt: secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥 pubKeyPath: D:/rsa/rsa.pub # 公钥地址 priKeyPath: D:/rsa/rsa.pri # 私钥地址 expire: 360 # 过期时间,单位分钟 filter: allowPaths: - swagger - /api-docs - /user/login - /user/info - /user/register - /user/sendemail - /user/verifycode
5.4.2 配置类
FilterProperties
package com.czxy.zx.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Data @ConfigurationProperties(prefix = "sc.filter") public class FilterProperties { private List<String> allowPaths; }
JwtProperties
package com.czxy.zx.config; import com.czxy.zx.utils.RsaUtils; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.io.File; import java.security.PrivateKey; import java.security.PublicKey; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Configuration @ConfigurationProperties(prefix = "sc.jwt") @Data public class JwtProperties { private String secret; private String pubKeyPath; private String priKeyPath; private Integer expire; private PublicKey publicKey; private PrivateKey privateKey; @PostConstruct //初始化方法注解 public void init() { try { File pubFile = new File(pubKeyPath); File priFile = new File(priKeyPath); if(!pubFile.exists() || ! priFile.exists()) { RsaUtils.generateKey(pubKeyPath,priKeyPath,secret); } // 获得公钥和私钥对象 this.publicKey = RsaUtils.getPublicKey(pubKeyPath); this.privateKey = RsaUtils.getPrivateKey(priKeyPath); } catch (Exception e) { e.printStackTrace(); } } }
5.4.3 过滤器
package com.czxy.zx.filter;
import com.czxy.zx.config.FilterProperties; import com.czxy.zx.config.JwtProperties; import com.czxy.zx.domain.EduUser; import com.czxy.zx.utils.JwtUtils; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.annotation.Resource; import java.nio.charset.StandardCharsets; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Component @EnableConfigurationProperties(FilterProperties.class) public class LoginFilter implements GlobalFilter, Ordered { @Resource private FilterProperties filterProperties; @Resource private JwtProperties jwtProperties; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { try { //1 获得请求路径 ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); System.out.println(path); //2 白名单 List<String> allowPaths = filterProperties.getAllowPaths(); for (String allowPath : allowPaths) { if(path.contains(allowPath)) { // 放行 return chain.filter(exchange); } } //3 获得token String token = request.getHeaders().getFirst("X-Token"); //4 校验token JwtUtils.getObjectFromToken(token, jwtProperties.getPublicKey(), EduUser.class); //5.1 成功,放行 return chain.filter(exchange); } catch (Exception e) { e.printStackTrace(); //5.2 失败,返回提示`token失效` ServerHttpResponse response = exchange.getResponse(); // 响应状态 401 没有权限 response.setStatusCode(HttpStatus.UNAUTHORIZED); // 响应数据的编码 response.getHeaders().add("Content-Type","application/json;charset=UTF-8"); // 响应“没有权限”提示 DataBuffer wrap = response.bufferFactory().wrap("没有权限".getBytes(StandardCharsets.UTF_8)); return exchange.getResponse().writeWith(Flux.just(wrap)); } } @Override public int getOrder() { return 1; } }
5.5 token 无效
MessageBox.confirm('没有权限,将重新登录', '重新登录提示框', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { store.dispatch('user/resetToken').then(() => { location.reload() }) })
6.作业:激活
6.1 分析
需求:
- 用户点击链接后可以进行账号激活,将用户的状态0改成1
- 用户重复点击,提示“账号已经激活,无需重复激活”
- 需要防止其他人帮着激活
- 激活成功了,跳转到登录页面,“账号已激活,请登录”
- 1天不激活,激活链接失效,需要重新发送
6.2 完善用户注册
@PostMapping("/register") public BaseResult register(@RequestBody EduUser eduUser) { //1 校验 // 1.1 密码 if(StringUtils.isBlank(eduUser.getPassword())) { ExceptionUtils.cast("密码不能为空"); } if(! eduUser.getPassword().equals(eduUser.getRepassword())) { ExceptionUtils.cast("确认密码和密码不一致"); } // 1.2 验证码 // 1) 获得redis验证码 String key = "register" + eduUser.getUsername() ; String redisVerifyCode = stringRedisTemplate.opsForValue().get(key); // 2) 删除redis验证码 stringRedisTemplate.delete(key); // 3) 无效 if(redisVerifyCode == null) { ExceptionUtils.cast("验证码无效"); } // 4) 不对 if(! redisVerifyCode.equalsIgnoreCase(eduUser.getVerifycode())) { ExceptionUtils.cast("验证码错误"); } //2 注册 boolean register = eduUserService.register(eduUser); //3 成功,发送激活邮件 if(register) { // 成功 // 获得uuid值 String uuidStr = UUID.randomUUID().toString().replace("-", ""); // 将uuid存放到redis中,设置有效时间 stringRedisTemplate.opsForValue().set(uuidStr,eduUser.getUsername(), 24, TimeUnit.HOURS); // 发送激活邮件 UserEmail userEmail = new UserEmail(); userEmail.setEmail(eduUser.getEmail()); userEmail.setTitle("XXX平台激活邮件"); // TODO 作业:用户激活 //String url = "http://localhost:10010/user-service/user/active?username=" + eduUser.getUsername(); String url = "http://localhost:9527/#/active?uuid=" + uuidStr; String msg = eduUser.getUsername() + ",你好:<br/>" + "<a href='"+url+"'>点击</a>链接进行账号激活。<br/>" + "如果不能点击,请复制下面的连接:" + url; userEmail.setText(msg); System.out.println(eduUser.getId()); //3 发送邮件-将邮件信息存放mq String jsonStr = JSON.toJSONString(userEmail); rabbitTemplate.convertAndSend("", RabbitEmailConfig.QUEUE_NAME, jsonStr); return BaseResult.ok("注册成功,请进行账号激活"); } return BaseResult.error("注册失败"); }
6.3 用户激活
6.3.1 前端实现
- 步骤:
- 步骤1:编写激活页面
- 步骤2:编写ajax函数
- 步骤3:添加白名单
- 步骤1:编写激活页面
- 步骤2:编写ajax函数
步骤3:添加白名单
6.3.2 后端实现
6.4 完善用户登录