架构师成长记_第七周_08_CAS单点登录系统构建
Posted _大木_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构师成长记_第七周_08_CAS单点登录系统构建相关的知识,希望对你有一定的参考价值。
CAS单点登录系统构建(初次访问13,14,15,16步骤)
#
继续完善controller
package com.beyond.controller;
import com.beyond.pojo.Users;
import com.beyond.pojo.vo.UsersVO;
import com.beyond.service.UserService;
import com.beyond.utils.BEYONDJSONResult;
import com.beyond.utils.JsonUtils;
import com.beyond.utils.MD5Utils;
import com.beyond.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import sun.util.calendar.BaseCalendar;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;
@Controller
public class SSOController
@Autowired
private UserService userService;
@Autowired
private RedisOperator redisOperator;
public static final String REDIS_USER_TOKEN = "redis_user_token";
public static final String REDIS_USER_TICKET = "redis_user_ticket";
public static final String REDIS_TMP_TICKET = "redis_tmp_ticket";
public static final String COOKIE_USER_TICKET = "cookie_user_ticket";
@GetMapping("/hello") // 因为上面用的是Controller注解,
@ResponseBody // 所有默认的GetMapping会认为是页面,我们应该在加上 @ResponseBody
public Object hello()
return "Hello World~";
@GetMapping("/login")
public Object login(String returnUrl,
Model model,
HttpServletRequest request,
HttpServletResponse response)
model.addAttribute("returnUrl", returnUrl);
// TODO 后续完善是否登录
// 用户从未登录过, 第一次进入则跳转到 CAS的统一登录界面
return "login";
/**
* CAS 的统一登录接口
* 目的: 1. 登录后创建用户的全局会话 -> uniqueToken
* 2. 创建用户全局门票, 用以表示在CAS端是否登录 -> userTicket
* 3. 创建用户的临时票据, 用于会跳回传 -> tmpTicket
*/
@PostMapping("/doLogin")
public String doLogin(String username, String password, String returnUrl, Model model, HttpServletRequest request, HttpServletResponse response) throws Exception
model.addAttribute("returnUrl", returnUrl);
if (StringUtils.isBlank(username) || StringUtils.isBlank(password))
model.addAttribute("errmsg", "用户名或者密码不能为空!");
return "login";
// 1. 实现登录
Users userResult = userService.queryUserForLogin(username, MD5Utils.getMD5Str(password));
if (userResult == null)
model.addAttribute("errmsg", "用户名或者密码不正确!");
return "login";
// 2. 实现用户的redis会话
String uniqueToken = UUID.randomUUID().toString().trim();
UsersVO usersVO = new UsersVO();
BeanUtils.copyProperties(userResult, usersVO);
usersVO.setUserUniqueToken(uniqueToken);
redisOperator.set(REDIS_USER_TOKEN + ":" + userResult.getId(),
JsonUtils.objectToJson(usersVO));
// 3. 生成ticket门票, 全局门票, 代表用户在CAS端登录过
String userTicket = UUID.randomUUID().toString().trim(); // trim 用于去掉首尾的空格
// 3.1 用户全局门票需要放到CAS端的cookie中
setCookie(COOKIE_USER_TICKET, userTicket, response);
// 4. userTicket 需要关联用户id, 并且放入到redis中, 代表这个用户有门票了, 可以在各个景区玩儿
redisOperator.set(REDIS_USER_TICKET + ":" + userTicket, userResult.getId());
// 5. 生成临时票据, 会跳到调用网址, 是由 CAS端所签发的一个一次性的临时ticket
String tmpTicket = createTmpTicket();
/**
* userTicket: 用于表示用户在CAS端的一个登录状态: 已经登录
* tmpTicket: 用于颁发给用户进行一次性的验证的票据, 有时效性
*/
return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
@PostMapping("/verifyTmpTicket")
@ResponseBody
public BEYONDJSONResult verifyTmpTicket(String tmpTicket,
HttpServletRequest request,
HttpServletResponse response) throws Exception
// 使用一次性临时票据来验证用户是否登录过, 如果登录过, 把用户会话信息返回给站点
// 使用完后, 需要销毁临时票据
String tmpTicketValue = redisOperator.get(REDIS_TMP_TICKET + ":" + tmpTicket);
if (StringUtils.isBlank(tmpTicketValue))
return BEYONDJSONResult.errorUserTicket("用户票据异常.");
// 0. 如果临时票据ok, 则需要销毁, 并且拿到 CAS端cookie中的全局userTicket, 以此在获取用户全局会话
if (!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket)))
return BEYONDJSONResult.errorUserTicket("用户票据异常.");
else
// 销毁临时票据
redisOperator.del(REDIS_TMP_TICKET + ":" + tmpTicket);
// 1. 验证并且获取用户的 userTicket
String userTicket = getCookie(request, COOKIE_USER_TICKET);
String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
if (StringUtils.isBlank(userId))
return BEYONDJSONResult.errorUserTicket("用户票据异常.");
// 2. 验证门票对应的 user会话是否存在
String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
if (StringUtils.isBlank(userRedis))
return BEYONDJSONResult.errorUserTicket("用户票据异常.");
// 验证成功, 返回OK, 携带用户会话
return BEYONDJSONResult.ok(JsonUtils.jsonToPojo(userRedis, UsersVO.class));
/**
* 创建临时票据
*
* @return
*/
private String createTmpTicket()
String tmpTicket = UUID.randomUUID().toString().trim();
try
redisOperator.set(REDIS_TMP_TICKET + ":" + tmpTicket, MD5Utils.getMD5Str(tmpTicket), 600);
catch (Exception e)
e.printStackTrace();
return tmpTicket;
private void setCookie(String key, String val, HttpServletResponse response)
Cookie cookie = new Cookie(key, val);
cookie.setDomain("sso.com");
cookie.setPath("/");
response.addCookie(cookie);
private String getCookie(HttpServletRequest request, String key)
Cookie[] cookieList = request.getCookies();
if (cookieList == null || StringUtils.isBlank(key))
return null;
String cookieVal = null;
for (int i = 0; i < cookieList.length; i++)
if (cookieList[i].getName().equals(key))
cookieVal = cookieList[i].getValue();
break;
return cookieVal;
设置跨域
package com.beyond;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig
public CorsConfig()
@Bean //@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名
public CorsFilter corsFilter()
//1. 添加cors配置信息
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:8080");
config.addAllowedOrigin("http://www.mtv.com");
config.addAllowedOrigin("http://www.mtv.com:8080");
config.addAllowedOrigin("http://www.music.com");
config.addAllowedOrigin("http://www.music.com:8080");
//设置是否发送 cookie 信息
config.setAllowCredentials(true);
//设置允许请求的方式
config.addAllowedMethod("*");
//设置允许的header
config.addAllowedHeader("*");
// 2. 为url 添加映射路径
UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
corsSource.registerCorsConfiguration("/**",config);
//3. 返回重新定义好的 corsSource
return new CorsFilter(corsSource);
测试
以上是关于架构师成长记_第七周_08_CAS单点登录系统构建的主要内容,如果未能解决你的问题,请参考以下文章
架构师成长记_第八周_20_Elasticsearch 集群构建
架构师成长记_第八周_20_Elasticsearch 集群构建