登录功能-微博登录、session数据共享(SpringSession)

08、单点登录与社交登录.pdf

登录功能

一.账户登录介绍

1.介绍

1.1 功能需求- 数据:用户名(手机)+密码

在这里插入图片描述

1.2 登录-数据的封装的实体类(auth)

@Data
public class UserLoginVo {
    //loginAccount
    private String loginacct;

    private String password;
}

1.3 登录(远程member)

1.3.1 登录的实体类
@Data
public class MemberLoginVo {
    private String loginacct;
    private String password;
}
1.3.2 登录的controller
    //登录服务
    @PostMapping("/login")
    public R login(@RequestBody MemberLoginVo vo) {
        MemberEntity entity=   memberService.login(vo);
        if (entity!=null){
            System.out.println("member的远程MemberController登录成功");
            return R.ok().put("member",entity);
        }else {
            System.out.println("MemberController登录失败");
            return R.error(BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getMsg());
        }
    }
1.3.3 登录的service实现类
    //2.登录服务
    @Override
    public MemberEntity login(MemberLoginVo vo) {
        String loginacct = vo.getLoginacct();
        String password = vo.getPassword();
        //1.去数据库查询
        QueryWrapper<MemberEntity> wrapper = new QueryWrapper<MemberEntity>().eq("username", loginacct).or().eq("mobile", loginacct);
        MemberEntity memberEntity = baseMapper.selectOne(wrapper);
        if (memberEntity == null) {
            System.out.println("登录失败!"+"数据库 数据不存在");
            return null;
        } else {
            //2.判断密码是否正确
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            boolean matches = passwordEncoder.matches(password, memberEntity.getPassword());
            if (matches) {
                System.out.println("member的实现类,登录成功");
                return memberEntity;
            } else {
                System.out.println("登录失败!"+"密码错误");
                return null;
            }
        }
    }

1.4 登录(auth)

1.4.1 登录的feign的service(auth)
@FeignClient("gulimall-member")
public interface MemberFeignService {
	//由于MemberLoginVo 与UserLoginVo 使用相同属性,所以使用auth的UserLoginVo 
    @PostMapping("/member/member/login")
	//public R login(@RequestBody MemberLoginVo vo)
     R login(@RequestBody UserLoginVo vo);
}
1.4.2 登录的controller(auth)
    //login 功能
    @PostMapping("/login")
    public String login(UserLoginVo vo,RedirectAttributes redirectAttributes){
        System.out.println("vo: "+vo);
        //远程登录member
        R r = memberFeignService.login(vo);
        if (r.getCode()==0){
            //成功
            System.out.println("auth的LoginController登录成功");
            return "redirect:http://gulimall.com";
        }else {
            System.out.println("auth的LoginController登录失败");
            Map<String, String> errors = new HashMap<>();
            errors.put("msg", r.getData("msg",new TypeReference<String>() {}));
            redirectAttributes.addFlashAttribute("errors", errors);
            return "redirect:http://auth.gulimall.com/login.html";
        }
    }
1.4.3 登录的页面渲染login.html(auth)

在这里插入图片描述

1.4.4 登录的测试 ok

二.社交登录介绍

1.介绍

在这里插入图片描述

2.OAuth2.0

 OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储
在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们 数据的所有内容。
 OAuth2.0:对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分 享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。
 官方版流程:
在这里插入图片描述
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
在这里插入图片描述

3.微博登陆 准备

3.1 进入微博开放平台

3.1.1 步骤

在这里插入图片描述

OAuth2.0授权服务,查看文档

3.1.2 修改login.html
1). 引导用户到授权页

https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI

<a href="https://api.weibo.com/oauth2/authorize?client_id=3133671081&response_type=code&redirect_uri=http://auth.gulimall.com/oauth2.0/weibo/success">
	<img style="width: 50px;height: 18px" src="/static/login/JD_img/weibo.png" />
</a>

client_id:设置自己的client_id
在这里插入图片描述redirect_uri:设置自己的redirect_uri
在这里插入图片描述

2). 用户授权成功,页面跳转至

YOUR_REGISTERED_REDIRECT_URI/?code=CODE

3) . 换取Access Token

login.html页面,点击微博登录按钮,先登录
在这里插入图片描述
获取:code
在这里插入图片描述
再 换取Access Token

https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE

其中client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET可以使用basic方式加入header中,返回值 在这里插入图片描述Access Token为:

在这里插入图片描述

4). 使用获得的Access Token调用API
5). 其他端口

在这里插入图片描述

6). 注意:

在这里插入图片描述

3.2 社交登录回调-代码

3.2.1 member项目
1).controller
      //社交登录-登录服务
    @PostMapping("/oauth2/login")
    public R oauthLogin(@RequestBody SocialUser socialUser) throws Exception {
        MemberEntity memberEntity= memberService.oauthLogin(socialUser);
        if (memberEntity!=null){
            //TODO 1.成功登录处理
            return R.ok().setData(memberEntity);
        }else {
            return R.error(BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getMsg());
        }
    }
2).service实现类MemberServiceImpl
    //3.社交登录-登录服务
    @Override
    public MemberEntity oauthLogin(SocialUser socialUser) throws Exception {
        //登录和注册合并逻辑
        String uid = socialUser.getUid();
        //1.判断当前社交用户,是否登录过系统
        MemberEntity memberEntity = baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid));
        if (memberEntity != null) {
            //此用户已经注册,更新数据
            MemberEntity update = new MemberEntity();
            update.setId(memberEntity.getId());
            update.setAccessToken(socialUser.getAccess_token());
            update.setExpiresIn(socialUser.getExpires_in());
            baseMapper.updateById(update);

            memberEntity.setAccessToken(socialUser.getAccess_token());
            memberEntity.setExpiresIn(socialUser.getExpires_in());
            return memberEntity;
        } else {
            //2.数据库没此数据,注册一个(说明没有登录过)
            MemberEntity register = new MemberEntity();
            //3.查询当前社交用户的社交账号信息(昵称性别等)--查询微博后台数据
            try {
                HashMap<String, String> query = new HashMap<>();
                query.put("access_token", socialUser.getAccess_token());
                query.put("uid", socialUser.getUid());
                //从后台获取用户的信息
                HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<String, String>(), query);
                if (response.getStatusLine().getStatusCode() == 200) {
                    //查询成功
                    String json = EntityUtils.toString(response.getEntity());
                    JSONObject jsonObject = JSON.parseObject(json);
                    //昵称
                    String name = jsonObject.getString("name");
                    String gender = jsonObject.getString("gender");
                    //省略其他信息....
                    register.setNickname(name);
                    register.setUsername(name);
                    register.setLevelId(1L);
                    register.setCreateTime(new Date());
                    register.setGender("m".equals(gender) ? 1 : 0);
                    //省略其他信息....
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            register.setSocialUid(socialUser.getUid());
            register.setAccessToken(socialUser.getAccess_token());
            register.setExpiresIn(socialUser.getExpires_in());
            System.out.println("register: "+register);

            baseMapper.insert(register);
            return register;
        }
    }
3.2.2 auth项目
1).feigin接口–(远程调用member的社交登录)
@FeignClient("gulimall-member")
public interface MemberFeignService {
    //社交登录-登录服务(微博)
    @PostMapping("/member/member/oauth2/login")
    public R oauthLogin(@RequestBody SocialUser socialUser) throws Exception;
}
2).MemberRespVo(登录后的响应数据)
@Data
@ToString
public class MemberRespVo implements Serializable {
	private Long id;
	/**会员等级id*/
	private Long levelId;
	/** 用户名*/
	private String username;
	/*** 密码 */
	private String password;
	/** * 昵称*/
	private String nickname;
	/*** 手机号码
	 */
	private String mobile;
	/**
	 * 邮箱
	 */
	private String email;
	/**
	 * 头像
	 */
	private String header;
	/**
	 * 性别
	 */
	private Integer gender;
	/**
	 * 生日
	 */
	private Date birth;
	/**
	 * 所在城市
	 */
	private String city;
	/**
	 * 职业
	 */
	private String job;
	/**
	 * 个性签名
	 */
	private String sign;
	/**
	 * 用户来源
	 */
	private Integer sourceType;
	/**
	 * 积分
	 */
	private Integer integration;
	/**
	 * 成长值
	 */
	private Integer growth;
	/**
	 * 启用状态
	 */
	private Integer status;
	/**
	 * 注册时间
	 */
	private Date createTime;

	/** 社交登录UID
	 */
	private String socialUid;
	/** 社交登录TOKEN
	 */
	private String accessToken;
	/** 社交登录过期时间
	 */
	private Long expiresIn;
}

3).Oauth2Controller
	 @Autowired
	 private MemberFeignService memberFeignService;
 
    //社交登录
    @GetMapping("/oauth2.0/weibo/success")
    public String weibo(@RequestParam("code") String code, HttpSession session) throws Exception {
        Map<String, String> map = new HashMap<>();
        map.put("client_id","3133671081");
        map.put("client_secret","99b103a33786d6af71338bc796bc242f");
        map.put("grant_type","authorization_code");
        map.put("redirect_uri","http://auth.gulimall.com/oauth2.0/weibo/success");
        map.put("code",code);
        System.out.println("code: "+code);

        //1、根据用户授权返回的code换取access_token
        HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<>(), map, new HashMap<>());

        //2、处理
        System.out.println("response.getStatusLine().getStatusCode(): "+response.getStatusLine().getStatusCode());
        if (response.getStatusLine().getStatusCode() == 200) {
            //获取到了access_token,转为通用社交登录对象
            String json = EntityUtils.toString(response.getEntity());
            //String json = JSON.toJSONString(response.getEntity());
            SocialUser socialUser = JSON.parseObject(json, SocialUser.class);

            //知道了哪个社交用户
            //1)、当前用户如果是第一次进网站,自动注册进来(为当前社交用户生成一个会员信息,以后这个社交账号就对应指定的会员)
            //登录或者注册这个社交用户
            System.out.println(socialUser.getAccess_token());
            //调用远程服务
            R oauthLogin = memberFeignService.oauthLogin(socialUser);
            if (oauthLogin.getCode() == 0) {
                MemberRespVo data = oauthLogin.getData("data", new TypeReference<MemberRespVo>() {});
                log.info("登录成功:用户信息:{}",data.toString());
                //2、登录成功跳回首页
                return "redirect:http://gulimall.com";
            } else {
                System.out.println("登录失败1");
                return "redirect:http://auth.gulimall.com/login.html";
            }

        } else {
            System.out.println("登录失败");
            return "redirect:http://auth.gulimall.com/login.html";
        }

    }
4).login.html
<li> 
	<a href="https://api.weibo.com/oauth2/authorize?client_id=3133671081&response_type=code&redirect_uri=http://auth.gulimall.com/oauth2.0/weibo/success">
		<img style="width: 50px;height: 18px" src="/static/login/JD_img/weibo.png" />
	</a>
</li>
5).测试,ok

点击登录,跳转登录页面,点击微博,授权后,登录成功,返回商品首页(gulimall.com)

4.session的数据共享-跨域

4.1 问题描述。登录成功后在产品首页数据显示问题,如何携带数据进行跨域?

当社交登录微博成功之后,会跳转到产品首页(gulimall.com),(auth项目—>product项目)。那么如何携带数据,首页显示登录后的数据呢?

4.2 解决方案

4.2.1.方案一:session,不合适,session不能在不同域名之间共享

由于微博登录后,从auth项目,跳转到product项目,跨了域名。因为session不能跨域名共享,所以session不可用。

1).session共享原理

在这里插入图片描述
在这里插入图片描述

2).session的问题
a.session不能跨不同域共享
b.同一个服务,复制多份,session不同步问题
c.不同服务,session不能共享问题
4.2.2.方案二:分布式session
1).问题

在这里插入图片描述

2).session共享问题解决
a. session复制(占内存,不可取)

session传输需要时间,占内存
在这里插入图片描述

b. 客户端存储(不安全,不可取)

不安全,cookie长度有限制,不能保存大量信息
在这里插入图片描述

c. hash一致性(有缺点,但可用)

缺点:web_server不能
在这里插入图片描述

d.统一存储(可用)–使用springSession

缺点:修改大量代码
优点:不担心服务器的水平扩展,服务器宕机问题
在这里插入图片描述

e.springSession整合了session统一存储
4.2.3.方案二:Springsession

5.SpringSession

SpringSession文档
HttpSession with Redis文档

1.介绍,子域向父域扩展

1.1 需求描述

auth的微博登录后,跳转到product的首页。auth.gulimall.com—>gulimall.com

1.2 问题描述,session共享问题,子域session共享

在这里插入图片描述

2.应用–auth、product

2.1.导入依赖
    <!--整合spring-session完成Session共享问题-->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>  
 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
2.2.配置
#配置redis
spring.redis.host=192.168.56.10
spring.redis.port=6379

#session保存到redis
spring.session.store-type=redis
#设置session过期时间
server.servlet.session.timeout=30m
2.3.启动类
@EnableRedisHttpSession     //整合Redis作为session存储
2.4.配置类
@Configuration
public class SessionConfig {
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("GULISESSION");
//        serializer.setCookiePath("/");
        serializer.setDomainName("gulimall.com");
        return serializer;
    }
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }
}
2.5.auth的controller使用session
public String weibo(@RequestParam("code") String code, HttpSession session){
	session.setAttribute("LOGIN_USER",data);
}
2.6.product的index.html使用session数据
你好,请登录: [[${session.LOGIN_USER==null?'':session.LOGIN_USER.nickname}]]

3.springSession原理

在这里插入图片描述

4.springSession其他操作

1).product的首页index.html页面修改
 <li>
   <a th:if="${session.loginUser!=null}" style="color: darkcyan">欢迎您,[[${session.loginUser==null?'':session.loginUser.nickname}]]</a>
   <a class="loginOut">退出</a>
   <a href="http://auth.gulimall.com/login.html" th:if="${session.loginUser==null}" style="color: crimson">请您登录!</a>
 </li>
  <li>
     <a  href="http://auth.gulimall.com/reg.html" class="li_2" th:if="${session.loginUser==null}" style="color: crimson">免费注册</a>
   </li>
2).product的首页index.html的退出操作
a.退出标签+js
<a class="loginOut">退出</a>
//登录退出,清除session

$(".loginOut").click(function () {
    var result = confirm("确定要退出吗?");
    if(result){
        location.href="http://auth.gulimall.com/logout";
    }
})
b.退出的方法
    //login退出 功能
    @RequestMapping("logout")
    public String tologout(HttpServletRequest request){
   	   /* 清除session的方案一 */
        Enumeration em = request.getSession().getAttributeNames();
        while(em.hasMoreElements()){
            request.getSession().removeAttribute(em.nextElement().toString());
            request.getSession().removeAttribute(AuthServerConstant.LOGIN_USER);
        }
        
 /* 清除session的方案二 */
//        HttpSession session = request.getSession();
//        session.invalidate();

        return "redirect:http://gulimall.com";
    }
3).登录跳转(如果已经登录成功,再次刷新登录页面,直接跳转到首页)
    //登录页
    @GetMapping("/login.html")
    public String loginPage(HttpSession session){
        Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
        if (attribute==null){
            //未登录。返回登录页
            return "login";
        }else {
            //登录成功,重定向到商品首页
            return "redirect:http://gulimall.com";
        }
    }
4).search的页面关于登录注册的修改
a.导入依赖
    <!--整合spring-session完成Session共享问题-->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>  
 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
b.配置
#配置redis
spring.redis.host=192.168.56.10
spring.redis.port=6379

#session保存到redis
spring.session.store-type=redis
#设置session过期时间
server.servlet.session.timeout=30m
c.启动类
@EnableRedisHttpSession     //整合Redis作为session存储
d.配置类
@Configuration
public class SessionConfig {
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("GULISESSION");
//        serializer.setCookiePath("/");
        serializer.setDomainName("gulimall.com");
        return serializer;
    }
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }
}
e.list.html修改 登录、注册
        <li>
            <a  class="li_2" th:if="${session.loginUser!=null}" style="color: cadetblue">欢迎您,[[${session.loginUser==null?'':session.loginUser.nickname}]]</a>
            <a href="http://auth.gulimall.com/login.html" class="li_2" th:if="${session.loginUser==null}" style="color: red">主人,请登录</a>
        </li>
        <li>
           <a href="http://auth.gulimall.com/reg.html" th:if="${session.loginUser==null}" style="color: red">免费注册</a>
        </li>

三.多系统登录-单点登录

1.介绍

在谷粒商城项目呢,实现了微博、账号登录,仅仅是单系统登录,多系统登录怎么办呢?
多系统登录,实现一个登录,多系统使用。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值