导航:

谷粒商城笔记+踩坑汇总篇

目录

5. 用户名密码登录

5.1【认证模块】登录业务

5.1.1 模型类,接收用户名密码

5.1.2 feign客户端新增登录功能

5.1.3 LoginController处理登录请求

6. 微博社交登录 (OAuth2.0)

6.1 OAuth2.0授权协议介绍

6.2 微博社交登录标准流程

6.3 业务实现

6.4 修改授权回调页

6.5 抽取社交用户信息模型类

6.6【会员模块】通过社交实体类登录业务

6.6.1 数据库表“ums_member”添加字段

6.6.2 MemberEntity添加字段

6.6.3 controller

6.6.4 service,传入社交用户uid、token等信息,返回会员实体类

6.7【认证模块】远程调用会员模块社交登录

6.8【认证模块】社交登录实现,处理社交登录授权后的请求

7.Session共享问题

7.1 session原理

7.2 分布式下session共享问题

7.3 Session共享问题的几种解决方案

7.3.1 Session复制(同步),不推荐

7.3.2 客户端存储,不推荐

7.3.3 hash一致性(某一用户永远都访问的是同一台服务器)

7.3.4 redis统一存储

7.3.5 提升作用域到父域名,子域session共享(推荐,使用)

7.4 SpringSession解决session共享问题

7.4.1 导入所需依赖

7.4.2 yml配置session存储方式和过期时间

7.4.3 启动类注解开启springsession

7.4.4 配置类设置session使用json序列化,并放大作用域(自定义)

7.4.5 启动测试

7.4.6 用户名密码登录成功时也存储session

7.4.7 检索模块获取session,跟上面步骤基本一样

8. xxl-sso分布式单点登录框架

8.1 介绍

8.1.1 多系统-单点登录SSO

8.1.2 xxl-sso分布式单点登录框架介绍

8.2 流程图

8.3 单点登录服务端gulimall-test-sso-server

8.3.1 创建步骤

8.3.2 pom文件(包含thymeleaf,redis)

8.3.3 配置application

8.3.4 新增LoginController

8.3.5 新增模板login.html

8.4 单点登录客户端(clien1)

8.4.1 创建步骤

8.4.2 pom文件

8.4.3 新增application配置

8.4.4 新增HelloController

8.4.5 新增模板employees.html

8.5 配置host

8.6 测试

8.7 总结

8.7.1实现一:在中央服务器登录并返回token机制

8.7.2 实现二:只要一个客户端在中央服务器登录,其他服务器也是登录状态

8.7.3 实现三:该接口由认证中心提供,远程调用传token查询数据【数据存储在redis中,跟Spring Session一样】


5. 用户名密码登录

5.1【认证模块】登录业务

5.1.1 模型类,接收用户名密码

package site.xx.gulimall.auth.vo;
@Data
public class UserLoginVo {private String loginacct;    //登录账号名private String password;
}

5.1.2 feign客户端新增登录功能

auth/feign/MemberFeignService.java

@FeignClient("gulimall-member")
public interface MemberFeignService {@PostMapping(value = "/member/member/register")R register(@RequestBody UserRegisterVo vo);@PostMapping(value = "/member/member/login")R login(@RequestBody UserLoginVo vo);}

5.1.3 LoginController处理登录请求

auth/controller/LoginController.java

/**** @param RedirectAttributes  放错误消息* @param session 将登录用户信息写入session并重定向到主页,右上角显示你好,Xxx。*  整合了SpringSession,扩大了session作用域到"gulimall.com",解决了session共享问题,* @return 路由到的页面,不加@ResponseBody,防止返回值是字符串*/@PostMapping(value = "/login")public String login(UserLoginVo vo, RedirectAttributes attributes, HttpSession session) {//远程登录R login = memberFeignService.login(vo);if (login.getCode() == 0) {
//登陆成功,将登录者信息放到session中,重定向到首页。MemberResponseVo data = login.getData("data", new TypeReference<MemberResponseVo>() {});session.setAttribute(AuthServerConstant.LOGIN_USER, data);return "redirect:http://gulimall.com";} else {
//登陆失败,将错误消息添加到attributes,重定向到登录页。Map<String,String> errors = new HashMap<>();errors.put("msg",login.getData("msg",new TypeReference<String>(){}));attributes.addFlashAttribute("errors",errors);return "redirect:http://auth.gulimall.com/login.html";}}

检测是否已登录:

    @GetMapping(value = "/login.html")public String loginPage(HttpSession session) {//从session先取出来用户的信息,判断用户是否已经登录过了Object attribute = session.getAttribute(LOGIN_USER);//如果用户没登录那就跳转到登录页面if (attribute == null) {return "login";} else {return "redirect:http://gulimall.com";}}

5.2【用户模块】 登录业务

5.2.1 模型类

package site.xx.gulimall.member.vo;
@Data
public class MemberUserLoginVo {private String loginacct;private String password;
}

5.2.2 controller

member/controller/MemberController.java

    /*** 登录接口*/@PostMapping(value = "/login")public R login(@RequestBody MemberUserLoginVo vo) {MemberEntity memberEntity = memberService.login(vo);if (memberEntity != null) {return R.ok().setData(memberEntity);} else {return R.error(BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCT_PASSWORD_EXCEPTION.getMsg());}}

5.2.3 service

MemberServiceImpl

    /*** 本地登录*/@Overridepublic MemberEntity login(MemberUserLoginVo vo) {String loginacct = vo.getLoginacct();String password = vo.getPassword();//1、去数据库查询 SELECT * FROM ums_member WHERE username = ? OR mobile = ?MemberEntity memberEntity = baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("username", loginacct).or().eq("mobile", loginacct));if (memberEntity == null) {//登录失败return null;} else {//获取到数据库里的passwordString password1 = memberEntity.getPassword();BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//进行密码匹配boolean matches = passwordEncoder.matches(password, password1);if (matches) {//登录成功return memberEntity;}}return null;}

6. 微博社交登录 (OAuth2.0)

6.1 OAuth2.0授权协议介绍

OAuth1.0: OAuth(开放授权) 是一个开放标准, 允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息, 而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。

OAuth2.0: 对于用户相关的 开放OpenAPI(例如获取用户信息, 昵称、头像、动态同步, 照片, 日志, 分享等) , 为了保护用户数据的安全和隐私, 第三方网站访问用户数据前都需要显式的向用户征求授权

流程

( A) 用户打开客户端以后, 客户端要求用户给予授权。
( B) 用户同意给予客户端授权。
( C) 客户端使用上一步获得的授权, 向认证服务器申请令牌。
( D) 认证服务器对客户端进行认证以后, 确认无误, 同意发放令牌。
( E) 客户端使用令牌, 向资源服务器申请获取资源。
( F) 资源服务器确认令牌无误, 同意向客户端开放资源。

注意点:

  1. 使用Code换取AccessToken,Code只能用一次
  2. 同一个用户的accessToken一段时间是不会变化的,即使多次获取

qq授权登录步骤:
1) 、 用户点击 QQ 按钮
2) 、 引导跳转到 QQ 授权页
3) 、 用户主动点击授权, 跳回之前网页。

6.2 微博社交登录标准流程

1.访问 https://open.weibo.com

2.微连接-网站接入-立即接入

3.创建新应用

4.填写应用信息

授权回调页:http://auth.gulimall.com/oauth2.0/weibo/success
        取消授权回调页:http://gulimall.com/fall

5.查看文档:https://open.weibo.com/wiki/授权机制说明

6.前端修改a标签跳转地址,使其跳转到微博授权页面。如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE,CODE码在路径尾部。

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

7.通过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中,返回值

8.Access Token和uid在响应里,代表登陆成功:

9.微博开放的权限:

获取用户信息:

6.3 业务实现

逻辑:

6.4 修改授权回调页

修改前端跳转授权页的url

6.5 抽取社交用户信息模型类

package com.xunqi.gulimall.auth.vo;
/*** @Description: 社交用户信息**/@Data
public class SocialUser {private String access_token;private String remind_in;private long expires_in;private String uid;private String isRealName;}

6.6【会员模块】通过社交实体类登录业务

6.6.1 数据库表“ums_member”添加字段

expires_in是token过期时间

6.6.2 MemberEntity添加字段

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

6.6.3 controller

package com.xx.gulimall.member.controller;
    @PostMapping(value = "/oauth2/login")public R oauthLogin(@RequestBody SocialUser socialUser) throws Exception {MemberEntity memberEntity = memberService.login(socialUser);if (memberEntity != null) {return R.ok().setData(memberEntity);} else {return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(),BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMessage());}}

6.6.4 service,传入社交用户uid、token等信息,返回会员实体类

  1. 如果注册过,就更新令牌、令牌过期时间
  2. 如果第一次登录,就通过社交账号的昵称、头像等信息新创建用户存入member数据库。
    @Overridepublic MemberEntity login(SocialUser socialUser) throws Exception {//具有登录和注册逻辑String uid = socialUser.getUid();//1、判断当前社交用户是否已经登录过系统MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid));if (memberEntity != null) {//这个用户已经注册过//更新用户的访问令牌的时间和access_tokenMemberEntity update = new MemberEntity();update.setId(memberEntity.getId());update.setAccessToken(socialUser.getAccess_token());update.setExpiresIn(socialUser.getExpires_in());this.baseMapper.updateById(update);memberEntity.setAccessToken(socialUser.getAccess_token());memberEntity.setExpiresIn(socialUser.getExpires_in());return memberEntity;} else {//2、没有查到当前社交用户对应的记录我们就需要注册一个MemberEntity register = new MemberEntity();//3、查询当前社交用户的社交账号信息(昵称、性别等)Map<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");String profileImageUrl = jsonObject.getString("profile_image_url");register.setNickname(name);register.setGender("m".equals(gender)?1:0);register.setHeader(profileImageUrl);register.setCreateTime(new Date());register.setSocialUid(socialUser.getUid());register.setAccessToken(socialUser.getAccess_token());register.setExpiresIn(socialUser.getExpires_in());//把用户信息插入到数据库中this.baseMapper.insert(register);}return register;}}

6.​​​​​​​7【认证模块】远程调用会员模块社交登录

认证模块

package com.xx.gulimall.auth.feign;
@FeignClient("gulimall-member")
public interface MemberFeignService {@PostMapping(value = "/member/member/register")R register(@RequestBody UserRegisterVo vo);@PostMapping(value = "/member/member/login")R login(@RequestBody UserLoginVo vo);@PostMapping(value = "/member/member/oauth2/login")R oauthLogin(@RequestBody SocialUser socialUser) throws Exception;@PostMapping(value = "/member/member/weixin/login")R weixinLogin(@RequestParam("accessTokenInfo") String accessTokenInfo);
}

6.8【认证模块】社交登录实现,处理社交登录授权后的请求

认证模块

@Slf4j
@Controller
public class OAuth2Controller {@Autowiredprivate MemberFeignService memberFeignService;/**** @param code    授权页授权后获取的code码* @param session 将登录用户信息写入session并重定向到主页,右上角显示你好,Xxx。*  整合了SpringSession,扩大了session作用域到"gulimall.com",解决了session共享问题,* @return* @throws Exception*/@GetMapping(value = "/oauth2.0/weibo/success")public String weibo(@RequestParam("code") String code, HttpSession session) throws Exception {//原作者信息:2077705774、40af02bd1c7e435ba6a6e9cd3bf799fd,同时修改login.html中的client_id。Map<String, String> map = new HashMap<>();map.put("client_id","588997645");map.put("client_secret","5d7746d10c4d926ed38692c8d17b7e31");map.put("grant_type","authorization_code");map.put("redirect_uri","http://auth.gulimall.com/oauth2.0/weibo/success");map.put("code",code);//1、根据用户授权返回的code换取access_tokenHttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<>(), map, new HashMap<>());//2、处理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)、memberFeignService.oauthLogin当前用户如果是第一次进网站,自动注册进来(为当前社交用户生成一个会员信息,以后这个社交账号就对应指定的会员)//登录或者注册这个社交用户System.out.println(socialUser.getAccess_token());//调用远程服务R oauthLogin = memberFeignService.oauthLogin(socialUser);if (oauthLogin.getCode() == 0) {MemberResponseVo data = oauthLogin.getData("data", new TypeReference<MemberResponseVo>() {});log.info("登录成功:用户信息:{}",data.toString());//1、第一次使用session,命令浏览器保存卡号,JSESSIONID这个cookie//以后浏览器访问哪个网站就会带上这个网站的cookie//TODO 1、默认发的令牌。当前域(解决子域session共享问题)//TODO 2、使用JSON的序列化方式来序列化对象到Redis中session.setAttribute(LOGIN_USER,data);//2、登录成功跳回首页return "redirect:http://gulimall.com";} else {return "redirect:http://auth.gulimall.com/login.html";}} else {return "redirect:http://auth.gulimall.com/login.html";}}}

HttpUtils之前已经导入过:

            /*** 重要提示如下:* HttpUtils请从* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java* 或者直接下载:* http://code.fegine.com/HttpUtils.zip* 下载** 相应的依赖请参照* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml* 相关jar包(非pom)直接下载:* http://code.fegine.com/aliyun-jar.zip*/

7.Session共享问题

7.1 session原理

session也是一种记录浏览器状态的机制,但与cookie不同的是,session是保存在服务器中

由于http是无状态协议,当服务器存储了多个用户的session数据时,如何确认http请求对应服务器上哪一条session,相当关键。这也是session原理的核心内容。

7.2 分布式下session共享问题

分布式场景下相同服务不同机器,session共享问题

根据session原理可知,session信息是保存在服务器内存的,虽然是相同服务但是在不同服务器,也不能做到session共享

分布式场景下不同服务session共享问题

因为获取session对象,是根据cookie中JSESSIONID来作为key获取session的,不同会话session ID不同,获取的session对象就会不同

7.3 Session共享问题的几种解决方案

7.3.1 Session复制(同步),不推荐

优点

  • web-server(Tomcat)原生支持,只需要修改配置文件

缺点

  • session同步需要数据传输,占用大量网络带宽,降低了服务器群的业务处理能力

  • 任意一台web-server保存的数据都是所有webserver的session总和,受到内存限制无法水平扩展更多的web-server

  • 大型分布式集群情况下,由于所有web-server都全量保存数据,所以此方案不可取。

  • 个人总结:使用方便,但是每台服务器都需要保存全量session数据,占用网络带宽(适合小型分布式)

7.3.2 客户端存储,不推荐

优点

  • 服务器不需存储session,用户保存自己的session信息到cookie中。节省服务端资源

缺点

  • 都是缺点,这只是一种思路

    具体如下:

    • 每次http请求,携带用户在cookie中的完整信息,浪费网络带宽

    • session数据放在cookie中,cookie有长度限制4K,不能保存大量信息

    • session数据放在cookie中,存在泄漏、篡改、窃取等安全隐患

这种方式不会使用

7.3.3 hash一致性(某一用户永远都访问的是同一台服务器)

方式一:利用用户ip地址来做负载均衡,使某一用户永远都访问的是同一台服务器

方式二:利用用户id来做负载均衡,使某一用户永远都访问的是同一台服务器

  • 优点:

    • 只需要改nginx配置,不需要修改应用代码

    • 负载均衡,只要hash属性的值分布是均匀的,多台web-server的负载是均衡的

    • 可以支持web-server水平扩展(session同步法是不行的,受内存限制)

  • 缺点

    • session还是存在web-server中的,所以web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登录

    • 如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session

  • 但是以上缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用

7.3.4 redis统一存储

  • 优点:

    • 没有安全隐患

    • 可以水平扩展,数据库/缓存水平切分即可

    • web-server重启或者扩容都不会有session丢失

  • 不足

    • 增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方式。redis获取数据比内存慢很多

    • 上面缺点可以用SpringSession完美解决

7.3.5 提升作用域到父域名,子域session共享(推荐,使用)

在存入session时jsessionid的作用域提升至最大.比如auth.gulimall.com->.gulimall.com,那么gulimall.com及其下面的所有子域名都可以拿到这个jsessionid,然后再去redis中查询对应的session信息,可以实现不同服务之间的session共享

相同服务之间的session共享使用,session存入redis即可解决问题,相同服务的域名是相同的jsessionid也是相同的

7.4 SpringSession解决session共享问题

文档:

7.4.1 导入所需依赖

     <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>

7.4.2 yml配置session存储方式和过期时间

spring:redis:host: 192.168.157.128port: 6379#使用redis存储sessionsession:store-type: redisserver:port: 20000servlet:#配置session过期时间session:timeout: 30m

7.4.3 启动类注解开启springsession

将该注解配置在认证模块主启动类GulimallAuthServerApplication 上或者配置类上

@EnableRedisHttpSession  //整合Redis作为session存储

 序列化异常报错

启动后,执行登录操作进行测试发现序列化异常:

解决:给响应模型类实现序列化接口,并把它移到common模块,以便于商品模块也能获取到:

7.4.4 配置类设置session使用json序列化,并放大作用域(自定义)

session默认使用jdk进行序列化,不方便阅读,建议修改为json

package site.xx.gulimall.auth.config;
@Configuration
public class GulimallSessionConfig {@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();//放大作用域到父域名cookieSerializer.setDomainName("gulimall.com");cookieSerializer.setCookieName("GULISESSION");cookieSerializer.setCookieMaxAge(60*60*24*7);return cookieSerializer;}@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer() {return new GenericJackson2JsonRedisSerializer();}
}

7.4.5 启动测试

修改前端,获取session内的值

清空Redis和session后重新启动测试、执行登录操作: 

首页可以拿到jsession,名称为配置类配置的名称:

Redis里session序列化方式现在是json,可读性强

7.4.6 用户名密码登录成功时也存储session

此时我们手动输入http://auth.gulimall.com/login.html仍然可以进入到登录页面再次进行登录,就需要在进入登录页面时进行判断,用户是否登录,如果已经用户登录直接重定向到首页,用户未登录才允许用户登录

用户的登录页面之前设置了视图映射这个必须要注释起来,视图映射是没有任何逻辑的,只要是这个请求就会跳到指定的视图(html),但是我们现在的登录页面是有逻辑判断的,需要在controller中新增

auth/controller/LoginController.java

    /*** 判断session是否有loginUser,没有就跳转登录页面,有就跳转首页*/@GetMapping(value = "/login.html")public String loginPage(HttpSession session) {//从session先取出来用户的信息,判断用户是否已经登录过了Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);//如果用户没登录那就跳转到登录页面if (attribute == null) {return "login";} else {return "redirect:http://gulimall.com";}}

在登录成功后手动输入http://auth.gulimall.com/login.html,自动回重定向至首页,清除session后,进入登录页面

7.4.7 检索模块获取session,跟上面步骤基本一样

哪个模块需要共享session就在哪个模块整合spring session。

导入依赖:

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 整合springsession --><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>

yml配置redis地址、session存储方式和过期时间

spring:redis:host: 192.168.157.128port: 6379#使用redis存储sessionsession:store-type: redisserver:port: 20000servlet:#配置session过期时间session:timeout: 30m

启动类注解开启springsession

@EnableRedisHttpSession  //整合Redis作为session存储

前端获取session:

测试成功

8. xxl-sso分布式单点登录框架

8.1 介绍

8.1.1 多系统-单点登录SSO

上文社交登录、SpringSession+扩大子域 只是单系统分布式集群的登录

单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的。

多系统-单点登录

​ 1、一处登录处处登录

​ 2、一处退出处处退出

例如登录微博,访问新浪视频、新浪体育页面,发现也已经登陆成功了。而两者域名是不同的:

8.1.2 xxl-sso分布式单点登录框架介绍

框架效果演示地址:https://gitee.com/xuxueli0323/xxl-sso

最重要的:中央认证服务器
核心:三个系统即使域名不一样,想办法给三个系统同步同一个用户的票据;
1)、中央认证服务器;ssoserver.com
2)、其他系统,想要登录去ssoserver.com登录,登录成功跳转回来
3)、只要有一个登录,其他都不用登录
4)、全系统统——个sso-sessionid;

8.2 流程图

8.3 单点登录服务端gulimall-test-sso-server

8.3.1 创建步骤

8.3.2 pom文件(包含thymeleaf,redis)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>site.zhourui</groupId><artifactId>gulimall-test-sso-server</artifactId><version>0.0.1-SNAPSHOT</version><name>gulimall-test-sso-server</name><description>单点登录的中央认证服务器</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

8.3.3 配置application

server.port=8080
#虚拟机地址,默认端口6379,不用配置
spring.redis.host=192.168.157.128

8.3.4 新增LoginController

gulimall-test-sso-server/src/main/java/site/zhourui/gulimall/ssoserver/controller/LoginController.java

package site.xx.gulimall.ssoserver.controller;
@Controller
public class LoginController {@AutowiredStringRedisTemplate redisTemplate;@ResponseBody@GetMapping("/userinfo")public String userinfo(@RequestParam(value = "token") String token) {String s = redisTemplate.opsForValue().get(token);return s;}@GetMapping("/login.html")public String loginPage(@RequestParam(value = "redirect_url",required = false) String url, Model model, @CookieValue(value = "sso_token", required = false) String sso_token) {if (!StringUtils.isEmpty(sso_token)) {return "redirect:" + url + "?token=" + sso_token;}model.addAttribute("url", url);return "login";}@PostMapping(value = "/doLogin")public String doLogin(@RequestParam("username") String username,@RequestParam("password") String password,@RequestParam("redirect_url") String url,HttpServletResponse response) {//登录成功跳转,跳回到登录页if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {String uuid = UUID.randomUUID().toString().replace("_", "");redisTemplate.opsForValue().set(uuid, username);Cookie sso_token = new Cookie("sso_token", uuid);response.addCookie(sso_token);return "redirect:" + url + "?token=" + uuid;}return "login";}}

8.3.5 新增模板login.html

gulimall-test-sso-server/src/main/resources/templates/login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录页</title>
</head>
<body>
<form action="/doLogin" method="post">用户名:<input type="text" name="username" /><br />密码:<input type="password" name="password" /><br /><input type="hidden" name="redirect_url" th:value="${url}" /><input type="submit" value="登录">
</form>
</body>
</html>

8.4 单点登录客户端(clien1)

8.4.1 创建步骤

8.4.2 pom文件

client1pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>site.zhourui</groupId><artifactId>gulimall-test-sso-client</artifactId><version>0.0.1-SNAPSHOT</version><name>gulimall-test-sso-client</name><description>客户端-测试sso</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

8.4.3 新增application配置

server.port=8081spring.redis.host=192.168.157.128

8.4.4 新增HelloController

package site.xx.gulimall.ssoclient.controller;
/*** 测试单点登录*/
@Controller
public class HelloController {/*** 无需登录就可访问** @return*/@ResponseBody@GetMapping(value = "/hello")public String hello() {return "hello";}@GetMapping(value = "/employees")public String employees(Model model, HttpSession session, @RequestParam(value = "token", required = false) String token) {if (!StringUtils.isEmpty(token)) {RestTemplate restTemplate=new RestTemplate();ResponseEntity<String> forEntity = restTemplate.getForEntity("http://ssoserver.com:8080/userinfo?token=" + token, String.class);String body = forEntity.getBody();session.setAttribute("loginUser", body);}Object loginUser = session.getAttribute("loginUser");if (loginUser == null) {return "redirect:" + "http://ssoserver.com:8080/login.html"+"?redirect_url=http://client1.com:8081/employees";} else {List<String> emps = new ArrayList<>();emps.add("张三");emps.add("李四");model.addAttribute("emps", emps);return "employees";}}}

8.4.5 新增模板employees.html

gulimall-test-sso-client/src/main/resources/templates/employees.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>员工列表</title>
</head>
<body>
<h1>欢迎:[[${session.loginUser}]]</h1>
<ul><li th:each="emp:${emps}">姓名:[[${emp}]]</li>
</ul>
</body>
</html>

gulimall-test-sso-client2/src/main/resources/templates/employees.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>员工列表</title>
</head>
<body>
<h1>欢迎:[[${session.loginUser}]]</h1>
<ul><li th:each="emp:${emps}">姓名:[[${emp}]]</li>
</ul>
</body>
</html>

8.5 配置host

#单点登录
127.0.0.1 ssoserver.com127.0.0.1 client1.com127.0.0.1 client2.com

8.6 测试

将三个服务启动

访问client1的/employees路径,未登录直接跳转带ssoserver.con的登录页面

http://client1.com:8081/employees

访问client2的/boss路径,未登录直接跳转带ssoserver.con的登录页面

http://client2.com:8082/boss

在http://ssoserver.com:8080/login.html?redirect_url=http://client1.com:8081/employees下登录

登录成功

刷新http://ssoserver.com:8080/login.html?redirect_url=http://client2.com:8082/boss

或者访问[员工列表 http://client2.com:8082/boss

8.7 总结

8.7.1实现一:在中央服务器登录并返回token机制

1、创建中央服务器
2、创建客户端
3、访问http://client1.com:8081/employees => 跳转 http://ssoserver.com:8080/login.html
4、带上了参数
http://ssoserver.com:8080/login.html?redirect_url=http://client1.com:8081/employess
5、登录页面隐藏域放上url的值,doLogin登录完之后跳转
1)先将用户信息存储起来 redis
2)在将令牌传回给客户端redirect:http://client1.com:8081/employess?token=uuid
6、判断是否返回token,如果登录成功,则获得令牌了
1)判断是否有令牌【是否登录】
2)根据令牌去中央服务器请求用户信息

8.7.2 实现二:只要一个客户端在中央服务器登录,其他服务器也是登录状态

实现:使用cookie,浏览器缓存中央服务器的cookie,所以每次跳转中央服务器会给浏览器记录sso_token【用于多系统间sso】,然后当客户端请求跳转到中央服务器会查看cookie然后实现了免登陆,并且将cookie值返回给客户端作为token【客户端获得了token就可以免登陆了】

8.7.3 实现三:该接口由认证中心提供,远程调用传token查询数据【数据存储在redis中,跟Spring Session一样】

然后将数据保存到自己的session中。【其实spring session也可以在redis查完数据后往session放一份0.0】
不同点:增加了认证服务,解决了Cookie不可跨域的问题,都是用同一个域名的cookie

谷粒商城笔记+踩坑(17)——【认证模块】登录,用户名密码登录+微博社交登录+SpringSession+xxl-sso单点登录相关推荐

  1. 谷粒商城笔记+踩坑(19)——订单模块构建、登录拦截器

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.页面环境搭建 1.1 动静分离 1.2 hosts添加域名映射 1.3 配置网关和nacos 1.4 引导类开启注册发现和feign客户端 1.5 thym ...

  2. 谷粒商城笔记+踩坑(22)——库存自动解锁。RabbitMQ延迟队列

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1 业务流程,订单失败后自动回滚解锁库存 可靠消息+最终一致性方案 2[仓库服务]RabbitMQ环境准备 2.1 导入依赖 2.2 yml配置RabbitMQ ...

  3. 谷粒商城笔记+踩坑(18)——购物车

    导航: 谷粒商城笔记+踩坑汇总篇 目录 一.环境搭建 1.1.购物车模块初始化 1.2.动静资源处理 1.3.页面跳转配置 二.数据模型分析 2.1.购物车需求 2.1.1.离线购物车和在线购物车需求 ...

  4. 谷粒商城笔记+踩坑(6)——商品服务-属性及其关联分组

      导航: 谷粒商城笔记+踩坑汇总篇_谷粒商城笔记踩坑6_vincewm的博客-CSDN博客 目录 10.商品服务-属性(规格参数和销售属性) 10.1.新增属性时,新增属性和属性分组的关联关系 10 ...

  5. 谷粒商城笔记+踩坑(1)——架构、项目环境搭建、代码生成器

     导航: 谷粒商城笔记+踩坑汇总篇_谷粒商城笔记踩坑6_vincewm的博客-CSDN博客 目录 1.项目介绍 1.1 微服务架构图 1.2. 微服务划分图 2.项目环境搭建 2.1. 虚拟机搭建环境 ...

  6. 谷粒商城笔记+踩坑(15)——商品详情搭建+异步编排

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.搭建页面环境 1.1.配置 Nginx 和 网关 1.2.动静资源配置 1.3.搜索页到详情页跳转 2.模型类抽取和controller 2.1.分析首页需 ...

  7. 谷粒商城笔记+踩坑(9)——上架商品spu到ES索引库

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.ES回顾 2.ES整合商品上架 2.1.分析 2.2.创建sku的es索引库 2.2.1.两种索引库设计方案分析 2.2.2.最终选用的索引库方案,nest ...

  8. 谷粒商城笔记+踩坑(23)——定时关闭订单

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.定时关单 1.0.业务流程 1.1.创建交换机.队列以及之间的绑定 1.2.在订单创建成功时向MQ中 延时队列发送消息 1.3.在订单的关闭之后时向MQ发送 ...

  9. 谷粒商城开发踩坑及部分知识点大总结

    谷粒商城开发BUG踩坑及部分知识点大总结 基本上bug的出现位置和时间线都能够匹配 如果对你有帮助的话就点个赞哈 2022.6.28 github设置ssh免密登陆,以下代码在git bash上面输入 ...

最新文章

  1. android 53 ContentProvider内容提供者
  2. CVT1100 错误的修复 2009-10-12 11:38
  3. 纵横公路造价软件学习_20年最新公路造价实战培训课程
  4. Python dataframe列拆分多行与统计
  5. 拥有此神技,脚本调试从此与 echo、set、test 说分手
  6. 如何获取枚举字符串,值及遍历枚举(转)
  7. 【转】微服务架构下分布式事务方案
  8. 程序怎么在matlab运行不了,这个程序在MATLAB 7.0中为什么运行不起来 那个工具箱怎么装...
  9. java jdbc实验,实验八 Java-JDBC编程
  10. signature=27524ebaa2473e38aa641bf251dcf3cf,[Without Title]
  11. mac上利用minikube搭建kubernetes(k8s)环境
  12. 学术壁报模板_电子壁报 | 中华医学会第十四次全国妇产科学学术会议
  13. HTML中的img标签无法显示图片的解决方案
  14. html flash mp3播放器,网页实用最简单的flash mp3播放器代码-多样式
  15. java去除空格的函数_JAVA中去掉空格--trim函数
  16. FFmpeg学习(2)——视频文件大小压缩
  17. 云适配牵手中建信息 征战企业级市场信心十足
  18. 浅层神经网络回归预测,基于MATLAB。 模型包括BPNN,极限学习机(ELM)和Elman网络
  19. 3dsMax如何渲染模型
  20. Mac下彻底卸载node和npm

热门文章

  1. 毕业三周年,又一个离别季
  2. 愈挫愈勇,谁没个失败的经历与挫折
  3. C#(Csharp)基础教程(上)(菜鸟教程笔记)
  4. 在线制作数据库ER模型
  5. MySQL 8 的学习——4从表中检索信息
  6. iOS 支持webrtc的浏览器 bowser
  7. bigemap地图下载器 好用吗?
  8. 给工具栏按钮添加图标和文字
  9. oracle 倒库详细步骤,科二倒车入库操作步骤高清图解,一步一解读,非常实用!...
  10. 远距离激光多脉冲测距TDC2K8S芯片使用