zoukankan      html  css  js  c++  java
  • 单点登录(token,JWT)

    一.什么事单点登录?

    答:单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。

    二.单点登录三种常见方式:

    1.session广播机制实现:即session复制 

    2.使用cookie+redis实现:

       (1)在项目中任何一个模块进行登录,登录之后,把用户数据放到两个redis和cookie两个地方:

               1>redis:在key :生成唯一随机值(ip,用户id等),value:用户数据

               2>把redis里面生成的key值放到cookie里面。

       (2)访问项目其他模块,发送请求带着cookie进行发送,获取cookie,拿着cookie值进行做事情:

              1>获取到cookie值,到redis中进行查询,根据key值进行查询,如果查到用户数据就是登录。       

    3.使用token实现

       token:按照一定的规则生成字符串,字符串可以包含用户信息(Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码)。

      实现方式:

      (1)在项目的某个模块进行登录,登录之后,按照一定的规则生成字符串,将用户数据放到字符串中,将字符串进行返回:

                1>可以通过cookie进行返回

                2>可以通过地址栏进行返回

        (2)再访问项目中的其他模块,每次访问都在地址栏带着生成的字符串,在访问模块里面获取地址栏字符串,根据字符串获取用户信息,如果可以获取到,就是登录。

     三.JWT是什么?

         JWT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换

        使用起来就是,由服务端根据规范生成一个令牌(token),并且发放给客户端。此时客户端请求服务端的时候就可以携带者令牌,以令牌来证明自己的身份信息。

        简单来说,JWT就是已经定好了规则,可以使用JWT生成字符串,可以包含用户信息。

    四.JWT的规则

    eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOjYyNywiZXhwIjoxNTcwMDE0ODg1fQ.vPbQh4syxNCzkKXKPSM93LzzLqoJdzPDNeKz8tz9cFM4NzhIOdPrJcH2DG
    -9-9MCUufCgrAhhGjuo85GKV4bOQ

    1.JWT的头信息:

    {
      'typ': 'JWT',
      'alg': 'HS256'
    }

    2.有效载荷:主体部分(包含用户信息)

    3.签名哈希:防伪标志

    五.JWT的使用

    1.引入JWT依赖

            <!--JWT-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
            </dependency>

    2.编写JWT工具类

    package com.atguigu.commonutils;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jws;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Date;
    
    /**
     * @author helen
     * @since 2019/10/16
     */
    public class JwtUtils {
         //定义两个常量
        //token的过期时间
        public static final long EXPIRE = 1000 * 60 * 60 * 24;
        //秘钥
        public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
    
        /*生成token字符串的方法*/
        public static String getJwtToken(String id, String nickname){
    
            String JwtToken = Jwts.builder()
                    //设置token的头信息
                    .setHeaderParam("typ", "JWT")
                    .setHeaderParam("alg", "HS256")
                    //分类
                    .setSubject("guli-user")
                    //设置token的过期时间
                    .setIssuedAt(new Date())
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                    //设置token的主体信息,存储用户信息
                    .claim("id", id)
                    .claim("nickname", nickname)
                    //设置签名哈希
                    .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                    .compact();
    
            return JwtToken;
        }
    
        /**
         * 判断token是否存在与有效
         * @param jwtToken
         * @return
         */
        public static boolean checkToken(String jwtToken) {
            if(StringUtils.isEmpty(jwtToken)) return false;
            try {
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 判断token是否存在与有效
         * @param request
         * @return
         */
        public static boolean checkToken(HttpServletRequest request) {
            try {
                String jwtToken = request.getHeader("token");
                if(StringUtils.isEmpty(jwtToken)) return false;
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 根据token字符串获取会员id
         * @param request
         * @return
         */
        public static String getMemberIdByJwtToken(HttpServletRequest request) {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) {
                return "";
            }
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            Claims claims = claimsJws.getBody();
            return (String)claims.get("id");
        }
    }

     六.登录实现

    1.编写用户实体类

    package com.atguigu.educenter.entity;
    
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.annotation.IdType;
    import java.util.Date;
    
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import java.io.Serializable;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.experimental.Accessors;
    
    /**
     * <p>
     * 会员表
     * </p>
     *
     * @author testjava
     * @since 2020-10-28
     */
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @ApiModel(value="UcenterMember对象", description="会员表")
    public class UcenterMember implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        @ApiModelProperty(value = "会员id")
        @TableId(value = "id", type = IdType.ID_WORKER_STR)
        private String id;
    
        @ApiModelProperty(value = "微信openid")
        private String openid;
    
        @ApiModelProperty(value = "手机号")
        private String mobile;
    
        @ApiModelProperty(value = "密码")
        private String password;
    
        @ApiModelProperty(value = "昵称")
        private String nickname;
    
        @ApiModelProperty(value = "性别 1 女,2 男")
        private Integer sex;
    
        @ApiModelProperty(value = "年龄")
        private Integer age;
    
        @ApiModelProperty(value = "用户头像")
        private String avatar;
    
        @ApiModelProperty(value = "用户签名")
        private String sign;
    
        @ApiModelProperty(value = "是否禁用 1(true)已禁用,  0(false)未禁用")
        private Boolean isDisabled;
    
        @ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
        private Boolean isDeleted;
    
        @ApiModelProperty(value = "创建时间")
        @TableField(fill = FieldFill.INSERT)
        private Date gmtCreate;
    
        @ApiModelProperty(value = "更新时间")
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date gmtModified;
    
    
    }

    2.编写controller层

    package com.atguigu.educenter.controller;
    
    
    import com.atguigu.commonutils.R;
    import com.atguigu.educenter.entity.UcenterMember;
    import com.atguigu.educenter.service.UcenterMemberService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * <p>
     * 会员表 前端控制器
     * </p>
     *
     * @author testjava
     * @since 2020-10-28
     */
    @RestController
    @RequestMapping("/educenter/member")
    @CrossOrigin
    public class UcenterMemberController {
        @Autowired
        UcenterMemberService memberService;
    
        //登录
        @PostMapping("/login")
        public R userLogin(@RequestBody UcenterMember member){
            //使用service中的方法实现登录
            //返回token值,使用jwt生成
            String token = memberService.login(member);
            return R.ok().data("token",token);
        }
    
    
    
        //注册
    
    
    }

    3.编写service层

    package com.atguigu.educenter.service;
    
    import com.atguigu.educenter.entity.UcenterMember;
    import com.baomidou.mybatisplus.extension.service.IService;
    
    /**
     * <p>
     * 会员表 服务类
     * </p>
     *
     * @author testjava
     * @since 2020-10-28
     */
    public interface UcenterMemberService extends IService<UcenterMember> {
        //实现登录
        String login(UcenterMember member);
    }

    4.编写serviceImpl

    package com.atguigu.educenter.service.impl;
    
    import com.atguigu.commonutils.JwtUtils;
    import com.atguigu.commonutils.MD5;
    import com.atguigu.educenter.entity.UcenterMember;
    import com.atguigu.educenter.mapper.UcenterMemberMapper;
    import com.atguigu.educenter.service.UcenterMemberService;
    import com.atguigu.servicebase.exceptionhandler.GuliException;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    /**
     * <p>
     * 会员表 服务实现类
     * </p>
     *
     * @author testjava
     * @since 2020-10-28
     */
    @Service
    public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
        //实现登录
        @Override
        public String login(UcenterMember member) {
            //获取手机号和密码
            String mobile = member.getMobile();
            String password = member.getPassword();
            //判断手机号和密码是否为空
            if(StringUtils.isEmpty(mobile)|| StringUtils.isEmpty(password)){
               throw new GuliException(20001,"手机号或者密码为空,登录失败");
            }
    
            //判断手机号是否正确
            QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
            wrapper.eq("mobile",mobile);
    
            UcenterMember mobileMember = baseMapper.selectOne(wrapper);
            //判断查询的对象是否为空
            if(mobileMember==null){//没有这个手机号
                throw new GuliException(20001,"手机号不存在,登录失败");
            }
    
            //判断密码是否正确
            /**因为数据库中的密码是加密的,
             * 所以需要先将传入的密码进行加密,
             * 再和数据库的密码进行比较,
             * 使用MD5加密*/
            if(!MD5.encrypt(password).equals(mobileMember.getPassword())){
                throw new GuliException(20001,"密码不正确,登录失败");
            }
    
            //判断用户是否禁用
            if(mobileMember.getIsDisabled()){
                throw new GuliException(20001,"用户已禁用,登录失败");
            }
            //登录成功,使用jwt工具类生成token
            String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
            return jwtToken;
        }
    }

    5.swagger测试

     六.注册功能

    1.编写实体类

    package com.atguigu.educenter.entity.vo;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    /**
     * author LiQinZhen
     * date 2020/10/29
     * description: 注册实体类
     */
    @Data
    @ApiModel(value="注册对象", description="注册对象")
    public class RegisterVo {
        @ApiModelProperty(value = "昵称")
        private String nickname;
    
        @ApiModelProperty(value = "手机号")
        private String mobile;
    
        @ApiModelProperty(value = "密码")
        private String password;
    
        @ApiModelProperty(value = "验证码")
        private String code;
    
    }

    2.在controller中创建注册的方法

        //注册
        @RequestMapping("/register")
        public R registerUser(@RequestBody RegisterVo registerVo){
            memberService.register(registerVo);
            return R.ok();
        }

    3.在service创建注册方法

    public interface UcenterMemberService extends IService<UcenterMember> {
        //实现登录
        String login(UcenterMember member);
        //注册
        void register(RegisterVo registerVo);
    }

    4.编写service的实现类Impl

    package com.atguigu.educenter.service.impl;
    
    import com.atguigu.commonutils.JwtUtils;
    import com.atguigu.commonutils.MD5;
    import com.atguigu.educenter.entity.UcenterMember;
    import com.atguigu.educenter.entity.vo.RegisterVo;
    import com.atguigu.educenter.mapper.UcenterMemberMapper;
    import com.atguigu.educenter.service.UcenterMemberService;
    import com.atguigu.servicebase.exceptionhandler.GuliException;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    /**
     * <p>
     * 会员表 服务实现类
     * </p>
     *
     * @author testjava
     * @since 2020-10-28
     */
    @Service
    public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
    
        //注入redis模板
    @Autowired RedisTemplate
    <String,String> redisTemplate; //实现登录 @Override public String login(UcenterMember member) { //获取手机号和密码 String mobile = member.getMobile(); String password = member.getPassword(); //判断手机号和密码是否为空 if(StringUtils.isEmpty(mobile)|| StringUtils.isEmpty(password)){ throw new GuliException(20001,"手机号或者密码为空,登录失败"); } //判断手机号是否正确 QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>(); wrapper.eq("mobile",mobile); UcenterMember mobileMember = baseMapper.selectOne(wrapper); //判断查询的对象是否为空 if(mobileMember==null){//没有这个手机号 throw new GuliException(20001,"手机号不存在,登录失败"); } //判断密码是否正确 /**因为数据库中的密码是加密的, * 所以需要先将传入的密码进行加密, * 再和数据库的密码进行比较, * 使用MD5加密*/ if(!MD5.encrypt(password).equals(mobileMember.getPassword())){ throw new GuliException(20001,"密码不正确,登录失败"); } //判断用户是否禁用 if(mobileMember.getIsDisabled()){ throw new GuliException(20001,"用户已禁用,登录失败"); } //登录成功,使用jwt工具类生成token String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname()); return jwtToken; } //注册方法 @Override public void register(RegisterVo registerVo) { //获取注册的数据 String code = registerVo.getCode();//验证码 String mobile = registerVo.getMobile();//手机号 String nickname = registerVo.getNickname();//昵称 String password = registerVo.getPassword();//密码 //非空判断 if(StringUtils.isEmpty(code)||StringUtils.isEmpty(mobile) ||StringUtils.isEmpty(nickname)||StringUtils.isEmpty(password)){ throw new GuliException(20001,"验证码,手机号,昵称或者密码为空,注册失败"); } //判断验证码是否正确,即发送到手机的验证码和数据库存的是否一样 //获取redis中的验证码 String redisCode = redisTemplate.opsForValue().get(mobile); //比较输入的验证码和redis中的验证码是否一样 if(!code.equals(redisCode)){ throw new GuliException(20001,"验证码不正确,注册失败"); } //判断手机号是否相同,如果表里面存在相同的手机号则不进行添加 //先根据手机号在数据库查询数据 QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>(); wrapper.eq("mobile",mobile); Integer count = baseMapper.selectCount(wrapper); if(count>0){ throw new GuliException(20001,"手机号已存在,注册失败"); } //数据添加到数据库 UcenterMember member = new UcenterMember(); member.setMobile(mobile); member.setNickname(nickname); member.setPassword(MD5.encrypt(password)); member.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132"); member.setIsDisabled(false); baseMapper.insert(member); } }

     5.根据token获取用户信息

    //根据token获取用户信息
        @GetMapping("/getMemberInfo")
        public R getMemberInfo(HttpServletRequest request){
            //调用jwt的方法,根据request对象获取头信息,返回用户id
            String memberId = JwtUtils.getMemberIdByJwtToken(request);
            //查询数据库,根据用户id获取用户信息
            UcenterMember member = memberService.getById(memberId);
            return R.ok().data("userInfo",member);
        }
  • 相关阅读:
    WPF Margin和Padding
    WPF Tab切换顺序设置
    WPF DataGrid DataGridTemplateColumn
    WPF CheckBox IsHitTestVisible
    WPF Tag
    WPF RadioButton
    WPF 用户控件(UserControl)
    WPF ToolTip
    Style Lessons in Clarity and Grace (11th Edition)中文翻译
    AI for AI
  • 原文地址:https://www.cnblogs.com/liqinzhen/p/13879724.html
Copyright © 2011-2022 走看看