Token 验证

在这之前先简单讲下登陆中验证码认证过程,先是在WebSecurityConfig里面配置下生成验证码的路径,使用谷歌的com.google.code.kaptcha API中的Producer对象生成验证码,后台生成Cookie 对象,key可以自定义,值用uuid去生成,然后把验证码存到redis中(key用的刚才uuid生成的那个值,value存验证码),然后把Cookie 返给前端。登录的时候传验证码过来,并且把刚才返的Cookie带过来,校验就根据cookie从redis中去取验证码。

public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setHeader(“Cache-Control”, “no-store, no-cache”);
response.setContentType(“image/jpeg”);

    //生成文字验证码String text = producer.createText();//生成图片验证码BufferedImage image = producer.createImage(text);//保存sessionString uuid = UUID.randomUUID().toString();Cookie cookie = new Cookie(SystemConstants.KAPTCHA_SESSION_KEY, uuid);response.addCookie(cookie);redisUtils.set(uuid, text);ServletOutputStream out = response.getOutputStream();ImageIO.write(image, "jpg", out);
}

使用基于 Token 的身份验证方法,大概的流程是这样的:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

总的来说就是客户端在首次登陆以后,服务端再次接收http请求的时候,就只认token了,请求只要每次把token带上就行了,服务器端会拦截所有的请求,然后校验token的合法性,合法就放行,不合法就返回401(鉴权失败)。

乍的一看好像和前面的seesion-cookie有点像,seesion-cookie是通过seesionid来作为浏览器和服务端的链接桥梁,而token验证方式貌似是token来起到seesionid的角色。其实这两者差别是很大的。

  1. sessionid 他只是一个唯一标识的字符串,服务端是根据这个字符串,来查询在服务器端保持的seesion,这里面才保存着用户的登陆状态。但是token本身就是一种登陆成功凭证,他是在登陆成功后根据某种规则生成的一种信息凭证,他里面本身就保存着用户的登陆状态。服务器端只需要根据定义的规则校验这个token是否合法就行。
  2. session-cookie是需要cookie配合的,居然要cookie,那么在http代理客户端的选择上就是只有浏览器了,因为只有浏览器才会去解析请求响应头里面的cookie,然后每次请求再默认带上该域名下的cookie。但是我们知道http代理客户端不只有浏览器,还有原生APP等等,这个时候cookie是不起作用的,或者浏览器端是可以禁止cookie的(虽然可以,但是这基本上是属于吃饱没事干的人干的事)…,但是token 就不一样,他是登陆请求在登陆成功后再请求响应体中返回的信息,客户端在收到响应的时候,可以把他存在本地的cookie,storage,或者内存中,然后再下一次请求的请求头重带上这个token就行了。简单点来说cookie-session机制他限制了客户端的类型,而token验证机制丰富了客户端类型。
  3. 时效性。session-cookie的sessionid实在登陆的时候生成的而且在登出事时一直不变的,在一定程度上安全就会低,而token是可以在一段时间内动态改变的。
  4. 可扩展性。token验证本身是比较灵活的,一是token的解决方案有许多,常用的是JWT,二来我们可以基于token验证机制,专门做一个鉴权服务,用它向多个服务的请求进行统一鉴权。

下面就拿最常用的JWT(JSON WEB TOKEN)来说:

JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案,就是登陆成功后将相关信息组成json对象,然后对这个对象进行某中方式的加密,返回给客户端,客户端在下次请求时带上这个token,服务端再收到请求时校验token合法性,其实也就是在校验请求的合法性。

具体实现如下:

调用登录接口,先是进入拦截器匹配url,

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

    try {String servletPath = request.getServletPath();boolean isLogin= pathMatcher.match("/login", servletPath);if (isNoPermission(servletPath,"/swagger-ui.html","/swagger-resources/**","/v2/**",             "/webjars/**","/api/voucher/download/**","/project/sourceAttach/download/**")){filterChain.doFilter(request, response);return;}if(!isLogin){boolean isCaptcha=pathMatcher.match("/code/**", servletPath);if(!isCaptcha){validToken(request,response);}}else{/*** 验证码校验*/if (!"dev".equals(environment)){validKaptcha(request);}}}catch (BadCredentialsException e){authenticationFailureHandler.onAuthenticationFailure(request,response,e);return;}filterChain.doFilter(request, response);}

通过后就进行调用登陆器

/**

  • 自定义登录登录器,使用JSON登录
    */
    public class LoginAuthFilter extends UsernamePasswordAuthenticationFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthFilter.class);

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

     if ( !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}String  username = this.obtainUsername(request);String password = this.obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);this.setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);
    

    }

接着进行登录认证

/**

  • 登录认证逻辑
    */
    public class LoginAuthenticationManager implements AuthenticationManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthenticationManager.class);

    private UserService userService;

    public LoginAuthenticationManager(UserService userService){
    this.userService=userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

     if(authentication.getPrincipal() == null || authentication.getCredentials() == null) {throw new BadCredentialsException("登陆验证失败,用户名或密码为空");}String username = (String)authentication.getPrincipal();//查询用户信息User user=userService.loadUserByUsername(username);if(user==null){throw new BadCredentialsException("账号信息不存在");}String password = (String)authentication.getCredentials();Boolean passwordTrue=new BCryptPasswordEncoder().matches(password,user.getPassword());//比较密码是否一致if(!passwordTrue){throw new BadCredentialsException("密码不正确");}return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(), user.getAuthorities());
    

    }
    }

认证通过后就进入了自定义的 登录认证成功的handler进行生成token操作,并把token存进redis,然后返回给前端,这里还可以把用户相关信息封装好根据需要返给前端

@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Authentication authentication) throws IOException, ServletException {

    User sysUserEntity=(User) authentication.getPrincipal();User sysUser=new User();sysUser.setUserId(sysUserEntity.getUserId());sysUser.setUserName(sysUserEntity.getUserName());sysUser.setLoginUserName(sysUserEntity.getLoginUserName());//创建tokenString token= JwtTokenUtil.createJWT(sysUserEntity);redisUtils.set(token+sysUserEntity.getUserId(),token);//设置一下用户登录信息到redis中去redisUtils.set(sysUserEntity.getUserId(), JSON.toJSONString(sysUserEntity));JSONObject data = new JSONObject();data.put("code", SystemConstants.SUCCESS);data.put("msg", "登陆成功");data.put("data", sysUser);httpServletResponse.setHeader(SystemConstants.AUTHORIZATION,token);response(httpServletResponse, data);
}

对应的生成token逻辑,这里使用jwt生成的

/**
* 构建jwt
* @param user
* @param
* @return
*/
public static String createJWT(User user) {
try {
// 使用HS256加密算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);//生成签名密钥byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(BASE64_BIARY);Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());//添加构成JWT的参数JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")// 可以将基本不重要的对象信息放到claims.setSubject(user.getUserName())           // 代表这个JWT的主体,即它的所有人.setIssuedAt(new Date())        // 是一个时间戳,代表这个JWT的签发时间;.setClaims(accountEntityfillMap(user)).signWith(signatureAlgorithm, signingKey);//生成JWTreturn builder.compact();} catch (Exception e) {log.error("签名失败", e);throw e;}
}public static Map<String, Object> accountEntityfillMap(User user) throws NullPointerException {Map<String, Object> info = new WeakHashMap<>();info.put("loginUserName", user.getLoginUserName());info.put("userId", user.getUserId());info.put("exTime",new Date());return info;
}

然后前端把登陆成功返的token存起来,后面每次调用接口的时候带上就可以了,调用接口的时候会先进拦截器,然后就是校验token逻辑
private void validToken(HttpServletRequest request, HttpServletResponse response) throws BadCredentialsException{

    boolean hasPower = false;//获取请求头String token=request.getHeader(SystemConstants.AUTHORIZATION);if(ObjectMapper==null){ObjectMapper = new ObjectMapper();}try {if(token ==null){throw new BadCredentialsException("token is empty");}//解析tokenClaims claims=JwtTokenUtil.parseJWT(token);String userId=claims.get("userId",String.class);if (userId == null || SecurityContextHolder.getContext().getAuthentication() == null) {//判断是否过期if(redisUtils.get(userId)==null){throw new BadCredentialsException("token 已过期");}//获取redis中的用户信息String userJson=redisUtils.get(userId);User user = JSON.parseObject(userJson,User.class);//判断权限if(user!=null && CollectionUtils.isEmpty(user.getPermissions())){throw new BadCredentialsException("当前请求没有权限,请联系管理员");}if(!hasPower && user.getPermissions() != null) {for (Permission ga : user.getPermissions()) {AntPathRequestMatcher matcher = new AntPathRequestMatcher(ga.getUri());if (hasPower=matcher.matches(request)) {break;}}}if(!hasPower) { //当前请求没有权限throw new BadCredentialsException("当前请求没有权限,请联系管理员");}UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));//设置为已登录SecurityContextHolder.getContext().setAuthentication(authentication);}}catch (BadCredentialsException e){log.error("拦截token验证", e);// ExceptionPrinterUtil.instance().write(response, e.getMessage(), "UTF-8");throw new BadCredentialsException(e.getMessage());}
}

/**
* 解析jwt
* @param jsonWebToken
* @param
* @return
*/
public static Claims parseJWT(String jsonWebToken) {
try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(BASE64_BIARY))
.parseClaimsJws(jsonWebToken).getBody();
return claims;
} catch (ExpiredJwtException eje) {
log.error("===== Token过期 =", eje);
throw eje;
} catch (Exception e){
log.error("= token解析异常 =====", e);
throw e;
}
}

token校验通过后,就可以访问具体接口,请求数据,并返给前端

Token方式 验证登录相关推荐

  1. 配置Open***使用User/Pass方式验证登录

    Open***和PPTP ***相比存在诸多的优势,最明显的是Open***支持NAT穿越,也就是说在nat环境下使用open***只需要一个在路由器上做一个端口映射即可!不需要其他路由的支持,要知道 ...

  2. 微信小程序python token验证_微信小程序登录对接Django后端实现JWT方式验证登录

    点击授权按钮后可以显示部分资料和头像,点击修改资料可以修改部分资料. 1.使用微信小程序登录和获取用户信息Api接口 2.把Api获取的用户资料和code发送给django后端 3.通过微信接口把co ...

  3. java 用户登录token_Java,SpringBoot采用token方式实现登录认证

    IT小奋斗2021-01-13 21:48:33 Token,令牌,访问资源的凭证,每次访问带上这个令牌,就可识别出用户身份. JWT (JsonWebToken),是实现token技术的一种解决方案 ...

  4. 数据采集-关于post生成ticket传递参数给另一个页面用get方式验证登录

    1.先输入错误密码看提交方式: 没有postform 而且还是get方式传输 有点奇怪 2.看源代码 可以看到登录页嵌入iframe 通过里面的链接传给另一个页面,点击 3,同第一步 可以看出 查看源 ...

  5. 二、SpringSecurity 自定义手机验证登录方式

    简介 在上一篇文章中,我们介绍了如何搭建一套基于SpringSecuity的项目框架,并且进行了演示,本文将继续扩展项目功能,实现自定义用户登录功能. 项目源码仓库:Gitee 代码分支:lesson ...

  6. 解读APP新一代验证登录方式——一键登录/免密认证

    近年来,验证领域迎来一个黑科技--用户在APP上进行账号注册或者号码绑定时,不需要接收短信验证码,直接可以以本机号码实现秒级验证.这种新颖且便捷的验证方式称为"一键认证". 一键认 ...

  7. 使用token验证登录信息,把token存到数据库中

    使用token验证登录信息,把生成的token存到数据库中,根据用户id判断是否重复登录,重复登录就重置到期时间 调用登录接口返回出token和uuid信息 每次访问都需要在Headers里面添加该数 ...

  8. 交换机配置ssh密码验证登录方式

    交换机配置ssh密码验证登录方式 一.背景: 1.由于PC机串口不支持热插拔,请不要在交换机带电的情况下,将串口插入或者拔出PC机.当连接PC和交换机时,请先安装配置电缆的DB-9端到PC机,再连接R ...

  9. JWT: 基于Token的验证

    现在SPA(Single Page Application, 单页面应用)和前后端分离已经是主流. 基于Token的验证非常适合这种构架. Difference between Token-based ...

最新文章

  1. Java 语言中十大“坑爹”功能!
  2. jQuery成为微软.NET开发工具的一部分了
  3. 使用css3制作正方形、三角形、扇形和饼状图
  4. 广告行业一些常用物料的尺寸
  5. iOS音频播放 (二):AudioSession 转
  6. 金融数据分析与挖掘实战1.7-1.8
  7. mysql 事务操作详细实例介绍
  8. bootstrap 点击图片放大查看_Bootstrap 开源 SVG 图标库 Bootstrap Icons
  9. 02 ZooKeeper分布式集群安装
  10. 【uniapp公众号分销商城从0到1】手机验证注册登录(04)
  11. itext7+poi实现excel转pdf
  12. Pytorch的一些小问题
  13. open sl java audio_webrtc学习(二): audio_device之opensles
  14. 由浅入深了解羚珑平台统一接入服务 —— Monet
  15. 一种有手就行的物联网平台的多终端接入方法
  16. 对当前医院奖金分配制度的探讨
  17. 20162316刘诚昊 第七周学习报告
  18. MacBook杀毒软件CleanMyMac X4.13
  19. 使用hardhat将合约部署到ganache
  20. 利用计算机阅读的文献是什么,文献检索作业

热门文章

  1. 让curl命令支持http2
  2. 图片缩放并设置dpi
  3. 产品学习(二)——竞品分析
  4. Android项目jenkins自动化构建之360加固(一)
  5. 自动填地址(拼多多)
  6. vscode多行注释
  7. html5 mac os 开发工具,如何通过TextMate或命令行在Mac OS上使用W3C tidy-html5软件包?...
  8. Apache NIFI 安装 ● 操作 ● 文件同步 ● oracle 数据库增量同步实例讲解
  9. AndroidRNFlutter实战——防抖节流函数
  10. MAXAR、Planet Labs、BlackSky Global——美国遥感卫星数据行业的巨头