1. 學習計劃 第十一天: 1、sso註冊功能實現 2、sso登錄功能實現 3、通過token獲得用戶信息 4、Ajax跨域請求(jsonp) 2. Sso系統工程搭建 需要創建一個sso服務工程,可以參考e3-manager創建。 e3-sso(pom聚合工程) |--e3-sso-interfa ...
1. 學習計劃
第十一天:
1、sso註冊功能實現
2、sso登錄功能實現
3、通過token獲得用戶信息
4、Ajax跨域請求(jsonp)
2. Sso系統工程搭建
需要創建一個sso服務工程,可以參考e3-manager創建。
e3-sso(pom聚合工程)
|--e3-sso-interface(jar)
|--e3-sso-Service(war)
e3-sso-web
3. 服務介面實現
3.1. 檢查數據是否可用
3.1.1. 功能分析
請求的url:/user/check/{param}/{type}
參數:從url中取參數1、String param(要校驗的數據)2、Integer type(校驗的數據類型)
響應的數據:json數據。e3Result,封裝的數據校驗的結果true:成功false:失敗。
業務邏輯:
1、從tb_user表中查詢數據
2、查詢條件根據參數動態生成。
3、判斷查詢結果,如果查詢到數據返回false。
4、如果沒有返回true。
5、使用e3Result包裝,並返回。
3.1.2. Dao層
從tb_user表查詢。可以使用逆向工程。
3.1.3. Service
參數:
1、要校驗的數據:String param
2、數據類型:int type(1、2、3分別代表username、phone、email)
返回值:e3Result
@Service public class UserServiceImpl implements UserService { @Autowired private TbUserMapper userMapper; @Override public e3Result checkData(String param, int type) { // 1、從tb_user表中查詢數據 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); // 2、查詢條件根據參數動態生成。 //1、2、3分別代表username、phone、email if (type == 1) { criteria.andUsernameEqualTo(param); } else if (type == 2) { criteria.andPhoneEqualTo(param); } else if (type == 3) { criteria.andEmailEqualTo(param); } else { return e3Result.build(400, "非法的參數"); } //執行查詢 List<TbUser> list = userMapper.selectByExample(example); // 3、判斷查詢結果,如果查詢到數據返回false。 if (list == null || list.size() == 0) { // 4、如果沒有返回true。 return e3Result.ok(true); } // 5、使用e3Result包裝,並返回。 return e3Result.ok(false); } }
發佈服務
3.1.4. 表現層
需要在e3-sso-web中實現。
引用服務
Controller
請求的url:/user/check/{param}/{type}
參數:從url中取參數1、String param(要校驗的數據)2、Integer type(校驗的數據類型)
響應的數據:json數據。e3Result,封裝的數據校驗的結果true:成功false:失敗。
@Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/user/check/{param}/{type}") @ResponseBody public e3Result checkData(@PathVariable String param, @PathVariable Integer type) { e3Result e3Result = userService.checkData(param, type); return e3Result; } }
3.2.1. 功能分析3.2. 用戶註冊
請求的url:/user/register
參數:表單的數據:username、password、phone、email
返回值:json數據。e3Result
接收參數:使用TbUser對象接收。
請求的方法:post
業務邏輯:
1、使用TbUser接收提交的請求。
2、補全TbUser其他屬性。
3、密碼要進行MD5加密。
4、把用戶信息插入到資料庫中。
5、返回e3Result。
3.2.2. Dao層
可以使用逆向工程。
3.2.3. Service層
參數:TbUser
返回值:e3Result
@Override public e3Result createUser(TbUser user) { // 1、使用TbUser接收提交的請求。 if (StringUtils.isBlank(user.getUsername())) { return e3Result.build(400, "用戶名不能為空"); } if (StringUtils.isBlank(user.getPassword())) { return e3Result.build(400, "密碼不能為空"); } //校驗數據是否可用 e3Result result = checkData(user.getUsername(), 1); if (!(boolean) result.getData()) { return e3Result.build(400, "此用戶名已經被使用"); } //校驗電話是否可以 if (StringUtils.isNotBlank(user.getPhone())) { result = checkData(user.getPhone(), 2); if (!(boolean) result.getData()) { return e3Result.build(400, "此手機號已經被使用"); } } //校驗email是否可用 if (StringUtils.isNotBlank(user.getEmail())) { result = checkData(user.getEmail(), 3); if (!(boolean) result.getData()) { return e3Result.build(400, "此郵件地址已經被使用"); } } // 2、補全TbUser其他屬性。 user.setCreated(new Date()); user.setUpdated(new Date()); // 3、密碼要進行MD5加密。 String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(md5Pass); // 4、把用戶信息插入到資料庫中。 userMapper.insert(user); // 5、返回e3Result。 return e3Result.ok(); }
發佈服務
3.2.4. 表現層
引用服務。
Controller:
請求的url:/user/register
參數:表單的數據:username、password、phone、email
返回值:json數據。e3Result
接收參數:使用TbUser對象接收。
請求的方法:post
@RequestMapping(value="/user/register", method=RequestMethod.POST) @ResponseBody public e3Result register(TbUser user) { e3Result result = userService.createUser(user); return result; }
3.2.5. 測試
可以使用restclient-ui-3.5-jar-with-dependencies.jar測試介面。
表單提交的content-type:application/x-www-form-urlencoded
3.3. 用戶登錄
3.3.1. 功能分析
請求的url:/user/login
請求的方法:POST
參數:username、password,表單提交的數據。可以使用方法的形參接收。
返回值:json數據,使用e3Result包含一個token。
業務邏輯:
登錄的業務流程:
登錄的處理流程:
1、登錄頁面提交用戶名密碼。
2、登錄成功後生成token。Token相當於原來的jsessionid,字元串,可以使用uuid。
3、把用戶信息保存到redis。Key就是token,value就是TbUser對象轉換成json。
4、使用String類型保存Session信息。可以使用“首碼:token”為key
5、設置key的過期時間。模擬Session的過期時間。一般半個小時。
6、把token寫入cookie中。
7、Cookie需要跨域。例如www.e3.com\sso.e3.com\order.e3.com,可以使用工具類。
8、Cookie的有效期。關閉瀏覽器失效。
9、登錄成功。
3.3.2. Dao層
查詢tb_user表。單表查詢。可以使用逆向工程。
3.3.3. Service層
參數:
1、用戶名:String username
2、密碼:String password
返回值:e3Result,包裝token。
業務邏輯:
1、判斷用戶名密碼是否正確。
2、登錄成功後生成token。Token相當於原來的jsessionid,字元串,可以使用uuid。
3、把用戶信息保存到redis。Key就是token,value就是TbUser對象轉換成json。
4、使用String類型保存Session信息。可以使用“首碼:token”為key
5、設置key的過期時間。模擬Session的過期時間。一般半個小時。
6、返回e3Result包裝token。
@Override public e3Result login(String username, String password) { // 1、判斷用戶名密碼是否正確。 TbUserExample example = new TbUserExample(); Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(username); //查詢用戶信息 List<TbUser> list = userMapper.selectByExample(example); if (list == null || list.size() == 0) { return e3Result.build(400, "用戶名或密碼錯誤"); } TbUser user = list.get(0); //校驗密碼 if (!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))) { return e3Result.build(400, "用戶名或密碼錯誤"); } // 2、登錄成功後生成token。Token相當於原來的jsessionid,字元串,可以使用uuid。 String token = UUID.randomUUID().toString(); // 3、把用戶信息保存到redis。Key就是token,value就是TbUser對象轉換成json。 // 4、使用String類型保存Session信息。可以使用“首碼:token”為key user.setPassword(null); jedisClient.set(USER_INFO + ":" + token, JsonUtils.objectToJson(user)); // 5、設置key的過期時間。模擬Session的過期時間。一般半個小時。 jedisClient.expire(USER_INFO + ":" + token, SESSION_EXPIRE); // 6、返回e3Result包裝token。 return e3Result.ok(token); }
發佈服務
3.3.4. 表現層
引用服務:
Controller
請求的url:/user/login
請求的方法:POST
參數:username、password,表單提交的數據。可以使用方法的形參接收。
HttpServletRequest、HttpServletResponse
返回值:json數據,使用e3Result包含一個token。
業務邏輯:
1、接收兩個參數。
2、調用Service進行登錄。
3、從返回結果中取token,寫入cookie。Cookie要跨域。
Cookie二級功能變數名稱跨域需要設置:
1)setDomain,設置一級功能變數名稱:
.itcatst.cn
.e3.com
.e3.com.cn
2)setPath。設置為“/”
工具類放到e3-common工程中。
4、響應數據。Json數據。e3Result,其中包含Token。
@RequestMapping(value="/user/login", method=RequestMethod.POST) @ResponseBody public e3Result login(String username, String password, HttpServletRequest request, HttpServletResponse response) { // 1、接收兩個參數。 // 2、調用Service進行登錄。 e3Result result = userService.login(username, password); // 3、從返回結果中取token,寫入cookie。Cookie要跨域。 String token = result.getData().toString(); CookieUtils.setCookie(request, response, COOKIE_TOKEN_KEY, token); // 4、響應數據。Json數據。e3Result,其中包含Token。 return result; }
3.4. 通過token查詢用戶信息
3.4.1. 功能分析
請求的url:/user/token/{token}
參數:String token需要從url中取。
返回值:json數據。使用e3Result包裝Tbuser對象。
業務邏輯:
1、從url中取參數。
2、根據token查詢redis。
3、如果查詢不到數據。返回用戶已經過期。
4、如果查詢到數據,說明用戶已經登錄。
5、需要重置key的過期時間。
6、把json數據轉換成TbUser對象,然後使用e3Result包裝並返回。
3.4.2. Dao層
使用JedisClient對象。
3.4.3. Service層
參數:String token
返回值:e3Result
@Override public e3Result getUserByToken(String token) { // 2、根據token查詢redis。 String json = jedisClient.get(USER_INFO + ":" + token); if (StringUtils.isBlank(json)) { // 3、如果查詢不到數據。返回用戶已經過期。 return e3Result.build(400, "用戶登錄已經過期,請重新登錄。"); } // 4、如果查詢到數據,說明用戶已經登錄。 // 5、需要重置key的過期時間。 jedisClient.expire(USER_INFO + ":" + token, SESSION_EXPIRE); // 6、把json數據轉換成TbUser對象,然後使用e3Result包裝並返回。 TbUser user = JsonUtils.jsonToPojo(json, TbUser.class); return e3Result.ok(user); }
3.4.4. 表現層
請求的url:/user/token/{token}
參數:String token需要從url中取。
返回值:json數據。使用e3Result包裝Tbuser對象。
@RequestMapping("/user/token/{token}") @ResponseBody public e3Result getUserByToken(@PathVariable String token) { e3Result result = userService.getUserByToken(token); return result; }
作業3.4.5. 安全退出
需要根據token刪除redis中的key。
4. 登錄註冊頁面實現
4.1. 註冊功能
第一步:把靜態頁面添加到工程中。
‘
第二步:展示頁面。
請求的url:
登錄:/page/login
註冊:/page/register
參數:無
返回結果:邏輯視圖String
@Controller public class PageController { @RequestMapping("/page/register") public String showRegister() { return "register"; } @RequestMapping("/page/login") public String showLogin() { return "login"; } }
第三步:js處理。
4.2. 登錄功能
參考login.jsp
5. 登錄註冊頁面整合首頁
5.1. 首頁跳轉到登錄、註冊頁面
5.2. 首頁展示用戶名
1、當用戶登錄成功後,在cookie中有token信息。
2、從cookie中取token根據token查詢用戶信息。
3、把用戶名展示到首頁。
方案一:在Controller中取cookie中的token數據,調用sso服務查詢用戶信息。
方案二:當頁面載入完成後使用js取token的數據,使用ajax請求查詢用戶信息。
問題:服務介面在sso系統中。Sso.e3.com(localhost:8088),在首頁顯示用戶名稱,首頁的功能變數名稱是www.e3.com(localhost:8082),使用ajax請求跨域了。
Js不可以跨域請求數據。
什麼是跨域:
1、功能變數名稱不同
2、功能變數名稱相同埠不同。
解決js的跨域問題可以使用jsonp。
Jsonp不是新技術,跨域的解決方案。使用js的特性繞過跨域請求。Js可以跨域載入js文件。
5.3. Jsonp原理
5.4. Json實現
5.4.1. 客戶端
使用jQuery。
5.4.2. 服務端
1、接收callback參數,取回調的js的方法名。
2、業務邏輯處理。
3、響應結果,拼接一個js語句。
方法一:
方法二: