SSO 單點登入 的筆記
1 系統中的事務問題
1.1 Spring預設的事務策略
1.1.1 關於程式碼中的try-catch
說明:
由於程式碼中採用spring的宣告式的事務處理,所有程式設計師我需關注事務控制,統統交給spring管理.
Spring要求,如果出現了執行時異常,spring才會回滾事務.
如果在程式碼中對入庫操作添加了try-catch ,則spring容器不能接收異常資訊,所以不會回滾事務.
總結:tyr-catch不要加到入庫和更新操作上.儘量控制其範圍.
2 單點登入
2.1 單點登入介紹
2.1.1 什麼是單點登入
說明:使用者一次登入後,可以免密登入其相關係統
2.1.2 SSO分析
說明:
當用戶登陸QQ遊戲時 ,進行了登陸操作,當訪問QQ郵箱時,需要再次登入,因為Session沒有共享,是不同的物件.所以資料不能公用.
2.1.3 單點登入設計圖
2.1.4 單點登入說明:
1.當用戶第一次登陸時,先通過SSO單點登入系統進行登入操作.
2.根據使用者資訊查詢使用者資料驗證登入是否有效
3.如果使用者名稱和密碼都正確,則生成ticket.並將User物件轉化JSON資料
4.將ticket和UserJSON資料寫入redis快取中
5.當用戶登陸成功後,在cookie儲存ticket資訊.
6.當用戶再次訪問前臺系統時,首先根據ticke資訊,查詢redis快取伺服器.獲取使用者資料.
7.當用戶訪問購物車時,首先前臺會校驗,根據ticket查詢使用者資訊,如果使用者沒有登陸則轉向單點登入系統.
8.當用戶訪問訂單系統時,首先前臺會校驗,根據ticket查詢使用者資訊,如果沒有該使用者資訊,則轉向單點登入系統.
2.2 構建SSO單點登入伺服器
2.2.1 SSO專案說明
單點登入伺服器.需要操作資料庫,所以構建時需要Controller.Service.Mapper一同完成.
構建web專案.
2.2.2 構建web專案
選擇web骨架建立專案
引入jt-parentjar包
引入common
2.2.3 匯入tomcat外掛
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8093</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
為sso新增啟動項
2.2.4 修改Nginx實現轉向
說明:通過nginx實現sso.jt.com的轉發
#京淘專案單點登入
server {
listen80;
server_namesso.jt.com;
location/ {
proxy_passhttp://127.0.0.1:8093;
}
}
Nginx修改完成之後 重啟nginx
nginx -s reload
2.2.5 修改HOST檔案
新增host檔案.保證訪問sso.jt.com訪問本機地址.
2.3 匯入配置檔案
2.3.1 拷貝jt-manage的配置檔案
2.3.2 修改springMVC.xml
2.3.3 修改Spring的配置檔案
2.3.4 修改Mybatis配置檔案
修改pojo的包路徑 和mapper的包路徑
2.3.5 修改web.xml配置檔案
拷貝:manage.jt.com的web.xml配置檔案.到jt-sso單點登入中.
<?xml version="1.0"encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="jt-manage"version="2.5">
<display-name>jt-sso</display-name>
<!--配置監聽器啟動spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring/applicationContext*.xml</param-value>
</context-param>
<!--1.配置前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置載入SpringMVC.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring/springmvc.xml</param-value>
</init-param>
</servlet>
<!--
/ 規定
1.表示攔截全部的請求
2.攔截所有靜態資源js/css/image 後期配置放行
3.放行.jsp資源
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置全站亂碼解決 POST亂碼 -->
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!--定義預設字符集utf-8 -->
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3 登入業務邏輯
3.1 京淘前臺登入跳轉
3.1.1 登入/註冊的跳轉
@Controller
@RequestMapping("/user")
publicclass UserController {
///user/register.html實現登入和註冊頁面跳轉
///user/login.html
@RequestMapping("/{param}")
public String module(@PathVariable String param){
//轉向使用者登陸和註冊頁面
returnparam;
}
}
4 使用者註冊的校驗
4.1 頁面JS分析
4.1.1 JS查詢
說明:
url:http://sso.jt.com/user/check/admin123/1?r=0.26835501213441115&callback=jsonp1517465570159&_=1517465578846
說明:
頁面中通過JSONP的形式直接訪問SSO單點登入系統,校驗使用者名稱/密碼是否存在
4.2 SSO程式碼編輯
4.2.1 介面文件
請求方法 |
GET |
URL |
http://sso.jt.com/user/check/{param}/{type} |
引數 |
格式如:chenchen/1 其中chenchen是校驗的資料 Type為型別,可選引數1 username、2 phone、3 email |
示例 |
http://sso.jt.com/user/check/chenchen/1 |
返回值 |
{ status: 200 //200 成功,201 沒有查到 msg: “OK” //返回資訊訊息 data: false //返回資料true使用者已存在,false使用者不存在,可以 } |
|
|
4.2.2 編輯POJO物件
@Table(name = "tb_user")
public class User extends BasePojo{
@Id //表示主鍵資訊
@GeneratedValue(strategy=GenerationType.IDENTITY) //主鍵自增
private Long id; //使用者的Id
private String username; //使用者名稱
private String password; //密碼 採用MD5加密
private String phone; //電話
private String email; //郵箱
4.2.3 編輯Mapper介面
public interface UserMapper extends SysMapper<User>{
//查詢資料是否存在 將資料封裝為Map
int findCheckUser(@Param("param")String param,@Param("cloumn")Stringcloumn);
}
在mybatis檔案中編輯userMapper.xml對映檔案
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTDMapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jt.sso.mapper.UserMapper">
<!--使用#號獲取資料的值,一般建議使用,因為有預編譯的效用.
$符使用時,只會出現在以列名為引數的應用.
能用#號不用$
如果在列名的欄位中使用#{}取值,相當於
SELECT COUNT(*) FROM tb_user WHERE "username"= 'admin123'
-->
<select id="findCheckUser"resultType="int">
select count(*) from tb_user where ${cloumn}= #{param}
</select>
</mapper>
4.2.4 建立service介面和實現類
publicinterface UserService {
//校驗資料是否存在
Boolean findCheckUser(String param,Integer type);
}
4.2.5 編輯Service實現類
@Service
publicclass UserServiceImpl implementsUserService {
@Autowired
private UserMapper userMapper;
//Type為型別,可選引數1 username、2 phone、3 email
//SELECT COUNT(*)FROM tb_user WHERE username= 'admin123'
@Override
public Boolean findCheckUser(String param, Integer type) {
String cloumn = null;
switch (type) {
case 1: cloumn = "username"; break;
case 2: cloumn = "phone"; break;
case 3: cloumn = "email"; break;
}
//1,0
intcount = userMapper.findCheckUser(param,cloumn);
returncount ==1 ? true : false;
}
}
4.2.6 編輯Controller
//校驗使用者的註冊資訊
//url:http://sso.jt.com/user/check/admin123/1?callback=jsonp1517465570159&_=1517465578846
//1 username、2 phone、3 email
@RequestMapping("/check/{param}/{type}")
@ResponseBody
public Object checkUser(@PathVariableString param,@PathVariable Integer type,String callback){
try {
//根據傳遞的引數判斷資料是否存在
Boolean result = userService.findCheckUser(param,type);
//返回JSONP的資料
MappingJacksonValue jacksonValue=
new MappingJacksonValue(SysResult.oK(result));
jacksonValue.setJsonpFunction(callback);
returnjacksonValue;
} catch (Exception e) {
e.printStackTrace();
returnnull;
}
}
4.3 使用者的註冊
4.3.1 JS請求
http://www.jt.com/service/user/doRegister
4.4 使用者的登陸
4.4.1 登陸的思路
1.當用戶點選登陸操作時,跳轉到登陸頁面
2.使用者輸入使用者名稱和密碼後點擊登陸按鈕發出請求.進行登入操作.
3.瀏覽器發出請求:
http://www.jt.com/service/user/doLogin?r=0.582247581950398
4.根據url接收使用者名稱和密碼資料
5.再次校驗使用者名稱和密碼是否為空
6.轉向登入頁面(js中已將實現)
7.如果使用者名稱和密碼都不為null,則呼叫
8.UserService 通過httpClient方式傳送資料
9.如果獲取的ticket不為null.則表示登入操作成功.如果ticket為null,直接返回null.
將ticket資訊寫入到Cookie中
4.5 實現前臺的登陸操作
4.5.1 頁面分析JS
JS:http://www.jt.com/service/user/doLogin?r=0.582247581950398
4.5.2 編輯UserController
//使用者登陸 http://www.jt.com/service/user/doLogin?r=0.582247581950398
//通過login.jsp檢測登陸的username和password是否正確
@RequestMapping("/doLogin")
@ResponseBody
public SysResult doLogin(String username,String password,
HttpServletRequest request,HttpServletResponseresponse){
//判斷使用者名稱和密碼是否為null
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
return SysResult.build(201, "使用者名稱密碼不能為空");
}
//當前輸入的使用者名稱是正確的
try {
//獲取使用者的ticket
String ticket =
userService.findUserByUP(username,password);
//ticket不為空
if(!StringUtils.isEmpty(ticket)){
//如果ticket資料不為空 則寫入cookie
//Cookie[] cookies = request.getCookies();
//Cookie的名稱必須為 JT_TICKET
CookieUtils.setCookie(request, response, "JT_TICKET", ticket);
return SysResult.oK(ticket);
}
} catch (Exception e) {
e.printStackTrace();
}
return SysResult.build(201, "使用者登陸失敗");
}
4.5.3 編輯UserService
@Override
public String findUserByUP(String username, String password) {
String uri = "http://sso.jt.com/user/login";
Map<String, String> map = new HashMap<String,String>();
//注意不要有空格
map.put("username", username);
map.put("password", password);
try {
String resutJSON = httpClient.doPost(uri,map);
//判斷資料是否有效 將其轉化為Sysresult物件
SysResult sysResult =
objectMapper.readValue(resutJSON,SysResult.class);
//判斷SSO返回是否正確
if(sysResult.getStatus() == 200){
return (String) sysResult.getData();
}
} catch (Exception e) {
e.printStackTrace();
}
returnnull;
}
5 作業:
1. 試著完成使用者登陸操作.
2. 試寫使用者登陸回顯操作
說明:當用戶登陸成功後,頁面會發出請求.根據ticket從redis中查詢使用者資訊
請求路徑:http://sso.jt.com/user/query/ticket資訊
根據業務介面檔案,查詢使用者資訊.
最終實現效用: