提出问题:当我们登录多个系统或者软件时,每一个软件或者系统都需要登录都需要判断用户是否具有"鉴权",这样登录次数太多,登陆过来登陆过去的一次次的判断用户"鉴权"我们有没有感觉很麻烦也很好费时间呢,尤其是可能会因为这个让系统卡主的时候,是不是很让人崩溃,那我们可不可以在一个地方登录之后,用户在其他的软件就不需要再次获得"鉴权",这样我们是不是省事多了,不需要一次次的登录。
分析问题:那我们试想是不是可以有一个统一验证中心,登陆过一次之后统一验证中心会有一个所谓的"令牌"来记录。以后的每一次需要登录的时候去验证中心验证是否有这个"令牌",如果有就不需要再次登录直接返回登陆成功的页面,如果没有就返回登录页面,这样我们是不是就可以实现上面提出的问题了。经过查阅资料得知我们这种分析正是经典的SSO,即登陆一次之后在相互信任的系统之中无需再次登录,并且降低了安全风险和管理上的消耗!
案例演示:首先我们来看一下验证中心即服务器端如何搭建,首先需要一个登录页面:
<%@ page language="java" contentType="text/html; charset=UTF-8" import="lindasoft.services.UserService,lindasoft.services.impl.UserServiceImpl"
pageEncoding="UTF-8"%>
<html>
<body>
<form method="post" action="http://127.0.0.1:8084/login1">
用户名:<input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
service<input type="text" name="service">
<input type="submit" value="登录">
</form>
</body>
</html>
service用来确定是哪个系统要登录,1即为系统一,当然大家还可以有自己其他的判断方式
首先是看一下我们用来存储随机生成的cookie的name的集合的类:
public class CookieList {
public static List<String> COOK_List = new ArrayList<>();
}
然后是controller的实现:
@Controller
public class LoginController {
@Resource
private UserService userService;
@RequestMapping(value = "login")
public ModelAndView getLogin(HttpServletRequest request, HttpServletResponse response){
//拿到cookie
Cookie[] cookies = request.getCookies();
String service = request.getParameter("service");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("service",service);
if(cookies != null){
for(Cookie cookie : cookies){
//判断cookie里有没有令牌,有就返回成功的页面
for(String name :CookieList.COOK_List){
if(name.equals(cookie.getName())){
modelAndView.setViewName("success");
return modelAndView;
}
}
}
}
modelAndView.setViewName("index");
return modelAndView;
}
@RequestMapping(value = "/login1")
public void login( HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
List<User> userList = userService.select();
String service = request.getParameter("service");
//用来判断账号密码是否正确,如果正确会i++
int i =0;
for(User user1:userList) {
if (username.equals(user1.getUsername()) && password.equals(user1.getPassword())){
i++;
//账号密码正确就创建cookie,即令牌
String name = username+System.currentTimeMillis();
Cookie cookie = new Cookie(name, username);
response.addCookie(cookie);
//将成功的cookieName放入map中
CookieList.COOK_List.add(name);
try {
//重定向到登录成功页面
response.sendRedirect("success?service="+service);
} catch (IOException e) {
e.printStackTrace();
}
}
}
if(i==0){
try {
response.sendRedirect("/fail");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@RequestMapping(value = "success")
public void getSuccess(HttpServletResponse response,HttpServletRequest request){
String service = request.getParameter("service");
System.out.println(service);
if("1".equals(service)){
try {
//重定向到系统一的成功页面
response.sendRedirect("http://127.0.0.1:8081/success");
} catch (IOException e) {
e.printStackTrace();
}
}else if("2".equals(service)){
try {
//重定向到系统二的登陆成功页面
response.sendRedirect("http://127.0.0.1:8082/success");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
就这样服务器就搭建好了,端口我们定义为8084,然后我们开始搭建两个客户端分别问8081端口和8082端口。
下面我们来看一下端口8081客户端的搭建:
首先看一下filter的:
<filter>
<filter-name>ssoServerFilter</filter-name>
<filter-class>lindasoft.filter.SSOClientFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ssoServerFilter</filter-name>
<url-pattern>/index</url-pattern>
</filter-mapping>
index为登录页面,跳转到登录页面的时候就会拦截,进入到我们自己写的拦截代码当中。
public class SSOClientFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Cookie[] cookies = request.getCookies();
int i =0;
if(cookies != null){
for(Cookie cookie:cookies){
for(String name:CookieList.COOK_List){
if(name.equals(cookie.getName())) {
i++;
}
}
}
if(i >0){
response.sendRedirect("/success");
}else {
response.sendRedirect("http//:127.0.0.1:8084/login");
}
}
}
@Override
public void destroy() {
}
}
i还是用来判断cookie里令牌是否存在,存在即i++,返回登录成功页面,若不存在令牌,返回服务器的登录页面。
然后看一下controller的实现:
@Controller
public class LoginController {
@Resource
private UserService userService;
@RequestMapping(value = "login")
public void getLogin(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
int i = 0;
if (cookies != null) {
for (Cookie cookie : cookies) {
for(String name :CookieList.COOK_List){
if(name !=null){
if (name.equals(cookie.getName())) {
i++;
}
}
}
}
}
if(i >0){
try {
response.sendRedirect("success");
i=0;
} catch (IOException e) {
e.printStackTrace();
}
}else {
try {
response.sendRedirect("http://127.0.0.1:8084/login?service=2");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@RequestMapping(value = "success")
public String getSuccess(){
return "success";
}
}
这样客户端一就搭建好了,最后我们来搭建客户端二,客户端二与客户端一除了端口不一样,其他的fiter和controller都是一样的都不详细介绍了。
下面我们来测试一下是否成功呢:
启动三个tomacat然后访问http://127.0.0.1:8081/login
我们发现确实是跳转到了服务器的登录页面然后我们输入账号密码登录
我们发现登录成功了,然后我们访问http://127.0.0.1:8082/login
我们发现显示登录成功并没有转到登录页面,然后我们清空cookie先访问系统二在访问系统一看看效果:
我们发现一样成功了,这样说吗我们系统就是搭建成功了。
总结:
1、首先要写好服务器的登录页面
2、登陆成功后cookie存入令牌
3、客户端接收到登录请求时,用fiter拦截进入到自己写的fiter代码里判断
4、判断cookie里有没有令牌,有的话就重定向到登陆成功页面,没有的话就重定向到服务器的的登录页面。