diff --git a/java/web相关/2.spring boot SSO单点登录.md b/java/web相关/2.spring boot SSO单点登录.md index 949de53..b4815c2 100644 --- a/java/web相关/2.spring boot SSO单点登录.md +++ b/java/web相关/2.spring boot SSO单点登录.md @@ -1,22 +1,15 @@ --- -id: '2019-03-01-18-52' -date: '2019/03/01 18:52' -title: 'spring boot 基于JWT实现单点登录' -tags: ['spring-boot', 'SSO', '单点登录', 'jwt'] +id: "2019-03-01-18-52" +date: "2019/03/01 18:52" +title: "spring boot 基于JWT实现单点登录" +tags: ["spring-boot", "SSO", "单点登录", "jwt"] categories: - - 'java' - - 'spring boot学习' + - "java" + - "spring boot学习" --- -**本篇原创发布于:**[FleyX 的个人博客](http://tapme.top/blog/detail/2019-03-01-18-52) - -**源码:**[github](https://github.com/FleyX/demo-project/tree/master/1.SSO%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95) - -照例配个图: ![塞尔达,林克](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190301190816.png) -
我叫林克,不是塞尔达
-   最近我们组要给负责的一个管理系统 A 集成另外一个系统 B,为了让用户使用更加便捷,避免多个系统重复登录,希望能够达到这样的效果——用户只需登录一次就能够在这两个系统中进行操作。很明显这就是**单点登录(Single Sign-On)**达到的效果,正好可以明目张胆的学一波单点登录知识。 本篇主要内容如下: @@ -143,4 +136,6 @@ SSO 说:好嘞,这个地址是合法的可以送 jwt 过去,这就跳转 可以看到,在系统 A 登录系统后,系统 B,系统 C 都不再需要输入用户名密码进行登录。如果速度足够快甚至都注意不到调到 SSO 再跳回来的过程。 -  如果有什么问题,可以在下方留言,或者邮件给我。看到都会回复的。 +**源码:**[github](https://github.com/FleyX/demo-project/tree/master/1.SSO%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95) + +**本篇原创发布于:**[www.tapme.top/blog/detail/2019-03-01-18-52](https://www.tapme.top/blog/detail/2019-03-01-18-52) diff --git a/前端/react/react实战/1.react实战_聊天室-总览.md b/前端/react/react实战/1.react实战_聊天室-总览.md new file mode 100644 index 0000000..2782eed --- /dev/null +++ b/前端/react/react实战/1.react实战_聊天室-总览.md @@ -0,0 +1,20 @@ +--- +id: "20190625" +date: 2019/06/25 10:58:00 +title: "react实战之聊天室-1(总览)" +tags: ["react", "antd", "table"] +categories: + - "前端" + - "react" +--- + +一个合格的全栈开发怎么能不会react呢?所以从现在开始系统的学习react开发。目标:完成完成一个简易的多人聊天室,包含前后台。 + +基于`create-react-app`进行开发,选择这个有以下两个原因: + +- 从头开始配置webpack太复杂,不用了解的这么细致。 +- 也不使用`antd pro`,`umi.js`这类开箱即用的,隐藏了太多细节,也不考虑。 + +在学习过程中产生了如下的笔记: + +- [1.react环境搭建](https://www.tapme.top/blog/detail/20190626) \ No newline at end of file diff --git a/前端/react/react实战/2.react实战之多人聊天室-react环境搭建.md b/前端/react/react实战/2.react实战之多人聊天室-react环境搭建.md new file mode 100644 index 0000000..dcdfc2e --- /dev/null +++ b/前端/react/react实战/2.react实战之多人聊天室-react环境搭建.md @@ -0,0 +1,469 @@ +--- +id: "20190626" +date: 2019/06/26 10:58:00 +title: "2.react实战之多人聊天室-react环境搭建" +tags: ["react", "antd", "less", "create-react-web"] +categories: + - "前端" + - "react" +--- + +总集篇:[react 实战之多人聊天室](https://www.tapme.top/blog/detail/20190625) + +本篇是实战系列的第一篇,主要是搭建 react 开发环境,在`create-react-app`的基础上加上如下功能: + +- 按需引入 antd 组件库,支持主题定制 +- 支持 less 语法,并使用 css-module 形式 css +- 支持路由 +- 支持 http 请求 +- 支持 redux + +**注意**:需要 node 版本大于 8.0. + +## 创建 create-react-app + +1. 安装 + +```bash +npm install -g create-react-app +``` + +2. 创建 react 应用 + +```bash +create-react-app chat-room +``` + +生成的目录结构如下图所示: + +![目录结构](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190625160702.png) + +## 配置 antd,less + +有两种方法能够对其配置进行修改: + +- 通过`npm run eject`暴露出配置文件,进行配置修改,相比于下面的方法不太优雅,因此不考虑. +- 通过`react-app-rewired`覆盖配置. + +### 首先安装依赖 + +在 2.1.x 版本的 react-app-rewired 需要配合`customize-cra`来进行配置覆盖。所以需要安装如下依赖: + +- react-app-rewired ,配置覆盖 +- customize-cra ,配置覆盖 +- antd ,ui 库 +- babel-plugin-import ,按需引入 antd +- less ,less 支持 +- less-loader ,less 支持 + +代码如下: + +```bash +npm install --save react-app-rewired customize-cra antd babel-plugin-import less less-loader +``` + +### 修改 package.json + +用`react-app-rewired`替换掉原来的`react-scripts` + +```json +/* package.json */ +"scripts": { +- "start": "react-scripts start", ++ "start": "react-app-rewired start", +- "build": "react-scripts build", ++ "build": "react-app-rewired build", +- "test": "react-scripts test", ++ "test": "react-app-rewired test", +} +``` + +### 创建 config-overrides.js + +在项目根目录,也就是`package.json`的同级目录创建`config-overrides.js`文件.内容如下: + +```javascript +const { override, fixBabelImports, addLessLoader } = require("customize-cra"); + +module.exports = override( + fixBabelImports("import", { + libraryName: "antd", + libraryDirectory: "es", + style: true + }), + addLessLoader({ + localIdentName: "[local]--[hash:base64:5]", + javascriptEnabled: true, + modifyVars: { "@primary-color": "#1DA57A" } + }) +); +``` + +### 使用 css-module + +要使用 css-module 需要将 css 文件命名为`fileName.module.less`,然后就能在组件中引入了,如下: + +```javascript +import React, { Component } from "react"; +import { Button } from "antd"; +import styles1 from "./index.module.less"; + +class Hello extends Component { + render() { + return ( +
+ hello +
world
+ +
heihei
+
+ ); + } +} + +export default Hello; +``` + +## 配置路由 + +首先修改 src 目录结构。改成如下所示: + +![目录结构](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190627115439.png) + +目录解释: + +- assets: 存放图标,小图片等资源文件 +- components:存放公共组件 +- layout: 存放样式组件,用于嵌套路由和子路由中复用代码 +- pages: 存放页面组件 +- redux:存放 redux 相关 + - action: 存放 action + - reducer: 存放 reducer 操作 +- util: 工具类 + +删除`serviceWorker.js`文件,并在`index.js`中删除和它相管的代码。这个是和离线使用相关的。 + +然后安装`react-router`依赖: + +```bash +cnpm install --save react-router-dom +``` + +从路由开始就能体会到 react 一切都是 js 的精髓,react-router-dom 提供了一些路由组件来进行路由操作。本程序使用`history`路由。 + +首先修改`index.js`根组件放到``下,以开启 history 路由。代码如下: + +```javascript +// index.js +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; +import { BrowserRouter } from "react-router-dom"; + +const s = ( + + + +); + +ReactDOM.render(s, document.getElementById("root")); +``` + +然后路由的配置方式有很多种,这里采用代码的方式组织路由,并将将 App.jsx 作为路由配置中心。 + +先加入登录和主页的路由,主要代码如下: + +```javascript + +render() { + const mainStyle = { + fontSize: "0.16rem" + }; + return ( + +
+ + + + + {/* 当前面的路由都匹配不到时就会重定向到/404 */} + + +
+
+ ); +} +``` + +名词解释: + +- Switch: 该组件表示只匹配一个,匹配到后不再继续往下匹配 +- Route:路由组件 +- exact:表示完全匹配,如果开启这个,`/`只匹配`/`,否则匹配所有的路径 +- Redirect:重定向组件,当前面的都不匹配就会匹配这个(因为没有开启`exact`且 path 为`/`),然后重定向到`/404` + +后续用到嵌套路由时会更加深入的讲解路由相关。 + +## 配置 http 请求工具 + +http 请求工具这里选择的是`axios`。 + +首先安装依赖: + +```bash +cnpm install --save axios +``` + +然后编写工具类`util/httpUtil.js`,代码如下: + +```javascript +// httpUtil.js + +import { notification } from "antd"; +import axios from "axios"; + +//定义http实例 +const instance = axios.create({ + // baseURL: "http://ali.tapme.top:8081/mock/16/chat/api/", + headers: { + token: window.token + } +}); + +//实例添加拦截器 +instance.interceptors.response.use( + function(res) { + return res.data; + }, + function(error) { + console.log(error); + let message, description; + if (error.response === undefined) { + message = "出问题啦"; + description = "你的网络有问题"; + } else { + message = "出问题啦:" + error.response.status; + description = JSON.stringify(error.response.data); + //401跳转到登录页面 + } + notification.open({ + message, + description, + duration: 2 + }); + setTimeout(() => { + if (error.response && error.response.status === 401) { + let redirect = encodeURIComponent(window.location.pathname + window.location.search); + window.location.replace("/public/login?redirect=" + redirect); + } + }, 1000); + return Promise.reject(error); + } +); + +export default instance; +``` + +主要实现了如下功能: + +- 自动添加 token,设计前后端通过 jwt 做认证,因此每个请求都要加上 token +- 响应预处理,如果有错误,自动弹窗提示。如果响应码为 401,重定向到登录页面。 + +## 配置 redux + +redux 算是 react 的一大难点。这里我们可以把 redux 理解成一个内存数据库,用一个对象来存储所有的数据. + +对这个数据的修改有着严格的限制,必须通过 reducer 来修改数据,通过 action 定义修改的动作。 + +这里以用户登录数据为例。 + +### 定义 + +1. 首先定义 action,创建文件`redux/action/loginInfoAction.js`,代码如下: + +```javascript +// 定义登录信息在store中的名字 +export const DATA_NAME = "loginInfo"; + +//定义修改loginInfo type +export const CHANGE_LOGIN_INFO = "changeLoginStatus"; + +export const changeLoginInfo = (token, userInfo) => { + return { + type: CHANGE_LOGIN_INFO, + data: { + token, + userInfo + } + }; +}; +``` + +- CHANGE_LOGIN_INFO :定义操作类别 +- changeLoginInfo: 定义一个 action,在组件中调用,传入要修改的数据,在这里加上 type 上传递到 reducer 中处理. + +2. 定义 reducer,创建文件`redux/reducer/loginInfo.js`,代码如下: + +```javascript +import * as loginAction from "../action/loginInfoAction"; + +function getInitData() { + let token, userInfo; + try { + token = localStorage.getItem("token"); + userInfo = JSON.parse(localStorage.getItem("userInfo")); + } catch (e) { + console.error(e); + token = null; + userInfo = null; + } + window.token = token; + window.userInfo = userInfo; + return { + token, + userInfo + }; +} + +const LoginStatusReducer = (state = getInitData(), action) => { + switch (action.type) { + case loginAction.CHANGE_LOGIN_INFO: + return { ...action.data }; + default: + return state; + } +}; + +export default LoginStatusReducer; +``` + +- getInitData 方法用于初始化 userInfo 数据,这里写的比较复杂,会先从 localeStore 中取数据,然后挂载到 window 中,方便`httpUtil`中获取 token。 +- LoginStatusReducer 方法用于处理 action 中的数据,输出处理后的 loginInfo 数据。 + +3. 编写 reducer 汇总类(redux/reducer/index.js),所有 reducer 都要汇总到一个方法中,这样就能生成整个系统的 store 对象。代码如下: + +```javascript +import { combineReducers } from "redux"; +import { DATA_NAME } from "../action/loginInfoAction"; +import loginInfo from "./loginInfo"; + +const data = {}; +data[DATA_NAME] = loginInfo; + +const reducer = combineReducers(data); + +export default reducer; +``` + +4. 编写`redux/index.js`,这里生成真正的数据对象,代码如下: + +```javascript +import { createStore } from "redux"; +import reducer from "./reducer"; + +const store = createStore(reducer); + +export default store; +``` + +5. 最后将 store 绑定到根节点(App.js)中即可,修改部分如下: + +![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190627165936.png) + +![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190627170032.png) + +### 使用 + +这里以登录页为例,学习如何获取到 loginInfo 和修改 loginInfo. + +1. 创建登录页组件,`pages/public/Login/index.js` + 登录页代码如下: + +```javascript +import React, { Component } from "react"; +import queryString from "query-string"; +import { Button, Input, message } from "antd"; +import IconFont from "../../../components/IconFont"; +import styles from "./index.module.less"; +import { connect } from "react-redux"; +import { changeLoginInfo, DATA_NAME } from "../../../redux/action/loginInfoAction"; +import axios from "../../../util/httpUtil"; + +function mapStateToProps(state) { + return state[DATA_NAME]; +} + +function mapDispatchToProps(dispatch) { + return { + updateLoginInfo: (token, userInfo) => dispatch(changeLoginInfo(token, userInfo)) + }; +} + +class Login extends Component { + constructor(props) { + super(props); + this.state = { + username: "", + password: "" + }; + this.query = queryString.parse(window.location.search); + } + + usernameInput = e => { + this.setState({ username: e.target.value }); + }; + passwordInput = e => { + this.setState({ password: e.target.value }); + }; + + submit = () => { + axios.post("/public/login", this.state).then(res => { + localStorage.setItem("token", res.token); + localStorage.setItem("userInfo", JSON.stringify(res.userInfo)); + window.token = res.token; + window.userInfo = res.userInfo; + message.success("登录成功"); + this.props.updateLoginInfo(res.token, res.userInfo); + if (this.query.redirect) { + this.props.history.replace(decodeURIComponent(this.query.redirect)); + } else { + this.props.history.replace("/"); + } + }); + }; + + render() { + return ( +
+ // 省略其他部分 + + ... +
+ ); + } +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Login); +``` + +其中最关键的是下面三个部分: + +- mapStateToProps:本方法从整个 store 中获取需要的数据,传递到 Login 组件的 props 中。 +- mapDispatchToProps:本方法用于修改 store 数据,返回的函数对象也会绑定到 Login 组件的 props 中,其中的 dispath 参数,用于调用 reducer 中的处理函数,根据 changeLoginInfo 返回的 action。 +- connect 方法用于将上面两个函数和 Login 组件绑定起来,这样就能在 props 中获取到了。**如果还有 withRouter**,应将 withRouter 放在最外层。 + +目前登录访问的接口为 yapi 的 mock 数据,真正的后台代码将会在后面编写。 + +## 结尾 + +由于我也是刚开始系统学习 react,有问题的地方希望大家能够指出。 + +源码:[github](https://github.com/FleyX/ChatRoom),切换到 tag:`第一篇:环境搭建`,便可以看到到目前为止的源码。 + +**本文原创发布于:**[www.tapme.top/blog/detail/20190626](http://www.tapme.top/blog/detail/20190626) diff --git a/前端/react/1.antd table 设置单元格单行显示.md b/前端/react/其他/1.antd table 设置单元格单行显示.md similarity index 100% rename from 前端/react/1.antd table 设置单元格单行显示.md rename to 前端/react/其他/1.antd table 设置单元格单行显示.md