百度网站架构,做网站 图片侵权,网络营销的概念名词解释,wordpress文章排列两种常见的认证方案
基于Session认证
登录状态信息保存在服务器内存中#xff0c;若访问量增加#xff0c;单台节点压力会较大集群环境下需要解决集群中的各种服务器登录状态共享问题 解决方案#xff1a;将登录状态保存的Redis中#xff0c;从Redis中查找登录状态
基于…两种常见的认证方案
基于Session认证
登录状态信息保存在服务器内存中若访问量增加单台节点压力会较大集群环境下需要解决集群中的各种服务器登录状态共享问题 解决方案将登录状态保存的Redis中从Redis中查找登录状态
基于Token认证
登录状态保存在客户端服务器没有存储开销客户端发起的每个请求自身均携带登录状态所以即使后台为集群也不会面临登录状态共享的问题 Token登录方案 登录流程如下 登录管理使用的三个接口
获取图形验证码通过easy-captcha依赖引入工具登录通过jjwt-api jjwt-impl jjwt-jackson 依赖引入工具获取登录用户个人信息
为了使得所有受保护的接口增加验证JWT合法性逻辑通过HandlerInterceptor拦截实现
common模块中创建JWT工具类
public class JwtUtil{private static SecretKey secretKey Keys.hmacShaKeyFor(任意复杂字符串大于指定字节数.getBytes());public static String createToken(Long userId,String username){//官方设置用set自定义设置用claimString jwt Jwts.builder().setExpiration(new Date(System.currentTimeMillis()3600000)).setSubject(LOGIN_USER).claim(userId,userId).claim(username,username).signWith(secretKey,SignatureAlgorithm.HS256)//设置签名以及所使用的算法种类.compact();return jwt;}//校验token是否合法public static Claims parseToken(String token){if(tokennull){throw new LeaseException(ResultCodeEnum.ADMIN_LOGIN_AUTH);}try{JwtParser jwtParser JWTS.parserBuilder().setSigningKey(secretKey).build();JwsClaims claimsJws jwtParser.parse(token);return claimsJws.getBody();}catch(ExpiredJwtExpection e){throw new LeaseException(ResultCodeEnum.TOKEN_EXPIRED);}catch(JwtException e){throw new LeaseException(ResultCodeEnum.TOKEN_INVALID);}}
}①、Controller接口
Tag(name后台管理系统登录)
RestController
RequestMapping(/admin)
public class LogicController{Autowiredprivate LoginService service;Operation(summary获取图形验证码)GetMapping(login/captcha)public ResultCaptchaVo getCaptcha(){CaptchaVo result servcie.getCaptcha();return Result.ok(result);}Operation(summary登录)GetMapping(login)public ResultString login(RequestBody Login loginVo){String jwt service.login(loginVo);return Result.ok(jwt);}Operation(summary获取登录用户个人信息)GetMapping(info)public ResultSystemUserInfoVo info(RequestHeader(access-token) String token){//将请求的某个header绑定到tokenClaims claims JwtUtil.parseToken(token);Long userId claims.get(userId,Long.class);SystemUserInfoVo systemUserInfoVo service.getLoginUserInfoById(userId);return Result.ok(systemUserInfoVo);}
}②、接口及其实现类
Service
public class LoginServiceImpl implements LoginService{Autowiredpirvate String RedisTemplate stringRedisTemplate;Autowiredprivate SystemUserMapper systemUserMapper;Overridepublic CaptchaVo getCaptcha(){SpecCaptcha specCaptcha new SpecCaptcha(130,48,4);String code specCaptcha.text().toLowerCase();//Redis中的key命名——项目名:功能模块名 admin:loginString key admin:login: UUID.randomUUID();//后台管理系统登录模块stringRedisTemplate.opsForValue().set(key,code,60,TimeUnit.SECONDS)return new CaptchaVo(specCpatcha.toBase64(),key);}Overridepublic String login(LoginVo loginVo){if(loginVo.getCaptchaCode()null){throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_NOTFOUND);}//从Redis获取保存的验证码String code stringRedisTemplate.opsForValue().get(loginVo.getCaptchaKey());if(codenull){//验证码过期throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED);}if(!code.equals(loginVo.getCaptchaCode().toLowerCase())){throw new LeaseException(ResultCodeEnum.ADMIN_CAPTCHA_CODE_ERROR);}//LambdaQueryWrapperSystemUser queryWrapper new LambdaQueryWrapper();//queryWrapper.eq(SystemUser::getUsername,loginVo.getUsername());/**由于该接口使用通用查询会导致和实体类的属性注解selectfalse查询功能不显示密码字段的冲突该接口出现了空指针异常所以采用自定义查询*///SystemUser systemUser systemUserMapper.selectOne(queryWrapper);SystemUser systemUser systemUserMapper.selectOneByUsername(queryWrapper);if(systemUsernull){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR);}if(systemUser.getStatus()BaseStatus.DISABLE){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_DISABLED_ERROR);}if(systemUser.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword))){throw new LeaseException(ResultCodeEnum.ADMIN_ACCOUNT_ERROR);}//生成JWTreturn JwtUtil.createToken(systemUser.getId(),systemUser.getUsername());}Overridepublic SystemUserInfoVo getLoginUserInfoById(Long userId){SystemUser systemUser systemUserMapper.selectById(userId);SystemUserInfo systemUserInfoVo new SystemUserInfo();systemUserInfoVo.setName(systemUser.getName());systemUserInfoVo.setAvatarUrl(systemUser.getAvatarUrl());return systemUserInfoVo;}
}③、为登录之后才能访问的接口增加验证JWT合法性的逻辑拦截器
Component
public class AuthenticationInterceptor implements HandlerInterceptor{//Controller接口之前执行Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResposne response,Object handler)throw Exception{//从请求头获取tokenString token request.getHeader(access-token);//同前端约定好的键值对JwtUtil.parseToken(token);return true;}
}注册拦截器
Configuration
public class WebMvcConfiguration implements WebMvcConfigurer{Autowiredprivate AuthenticationIntercpetor authenticationIntercpetor;Overridepublic void addInterceptor(InterceptorRegistry registry){registry.addInterceptor(this.authenticationIntercpetor).addPathPatterns(/admin/**)//拦截admin下所有路径.excludePathPatterns(/admin/login/**);}
}关于拦截器的重复解析一次在拦截器中一次是controller接口中 JwtUtil.parseToken(token) 所以要通过拦截器的解析结果保存起来由于拦截器限制性通常将结果保存在ThreadLocal中
①、在common模块中创建工具类
public class LoginUserHolder{public static ThreadLocalLoginUser threadLocal new ThreadLocal();public static void setLoginUser(LoginUser loginUser){threadLocal.set(loginUser);}public static LoginUser getLoginUser(){return threadLocal.get();}public static void clear(){threadLocal.remove();}
}同时创建LoginUser
Data
AllArgsConstrctor
public class LoginUser{private Long userId;private String username;
}②、修改拦截器
Component
public class AuthenticationInterceptor implements HandlerInterceptor{Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throw Exception{String token request.getHeader(access-token);Claims claims JwtUtil.parseToken(token);Long userId claims.get(userId,Long.class);String username claims.get(username,String.class);LoginUserHolder.setLoginUser(new LoginUser(userId,username));return true;}//清理线程池的内容Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler){LoginUserHolder.clear();}
}
③、改造Controller
Operation(sumary获取登录用户个人信息)
GetMapping(info)
public ResultSystemUserInfoVo info(){Long userId LoginUserHolder.getLoginUser().getUserId();SystemUserInfoVo systemUserInfo service.getLoginUserInfoById(userId);return Result.ok(systemUserInfoVo);
}手机移动端登录的具体流程 ①、Controller
Tag(name登录管理)
RestController
RequestMapping(/app/)
public class LoginController{Autowiredprivate LoginService service;GetMapping(login/getCode)Operation(summary获取短信验证码)public Result getCode(RequestParam String phone){service.getCode(phone);return Result.ok();}PostMapping(login)Operation(summary登录)public ResultString login(RequestBody LoginVo loginVo){return Result.ok()}GetMapping(info)Operation(summary获取登录用户信息)public ResultUserInfoVo info(){retur Result.ok();}
}②、service
Ⅰ、登录的service及其实现类
public interface LoginService{void getCode(String phone);
}Service
public class LoginServiceImpl implements LoginService{Autowiredprivate SmsService smsService;Autowiredprivate StringRedisTemplate redisTemplate;Overridepublic void getCode(String phone){}
}Ⅱ、发送短信的service及其实现类
public interface SmsService{void sendCode(String phone,String code);
}Service
public class SmsServiceImpl implements SmsService{Autowiredprivate Client client;Overridepublic void sendCode(String phone,String code){SendSmsRequest request new SendSmsRequest();requst.setPhoneNumber(phone);request.setSignName(阿里云短信测试);request.setTemplateCode(SMS_154950909);request.setTemplateParam({\code\:\code\});try{client.sendSms(request);}catch(Exception e){throw new RuntimeException(e);}}
}调用阿里云短信服务common模块中依赖dysmsapi20170525 applicaiton.yml增加access-key-id账号 access-key-secret密码 endpoint 对配置类进行映射common模块中
Data
ConfigurationProperties(prefixaliyun.sms)
public class AliyunSMSProperties{private String accessKeysId;private String accessKeySecret;private String endpoint;
}将映射到的参数进行创建访问阿里云的客户端配置
Configuration
EnableConfigurationProperties(AliyunSMSProperties.class)
ConditionalOnProperty(namealiyun.sms.endpoint)//条件注解放置其他模块没有配置访问参数而报错
public class AliyunSMSConfiguration{Autowiredprivate AliyunSMSProperties properties;Beanpublic Client createClient(){Config config new Config();config.setAccessKeyId(properties.getAccessKeyId());config.setAccessKeySecret(properties.getAccessKeySecret());config.setEndpoint(properties.getEndpoint())try{return new Client(config);}catch(Exception e){throw new RuntimeException(e);}}
}