天天看點

springmvc的HandlerInterceptor的簡單了解(登入例子)

抽象的類

<code>package</code> <code>com.book.admin.interceptor;</code>

<code>import</code> <code>java.util.ArrayList;</code>

<code>import</code> <code>java.util.List;</code>

<code>import</code> <code>java.util.regex.Pattern;</code>

<code>import</code> <code>javax.servlet.http.HttpServletRequest;</code>

<code>import</code> <code>javax.servlet.http.HttpServletResponse;</code>

<code>import</code> <code>org.slf4j.Logger;</code>

<code>import</code> <code>org.slf4j.LoggerFactory;</code>

<code>import</code> <code>org.springframework.web.servlet.HandlerInterceptor;</code>

<code>import</code> <code>org.springframework.web.servlet.ModelAndView;</code>

<code>/**</code>

<code> </code><code>* </code>

<code> </code><code>* @author liweihan</code>

<code> </code><code>*</code>

<code> </code><code>*/</code>

<code>public</code> <code>abstract</code> <code>class</code> <code>AbstractInterceptor </code><code>implements</code> <code>HandlerInterceptor{</code>

<code>    </code> 

<code>    </code><code>private</code> <code>static</code> <code>Logger logger = LoggerFactory.getLogger(AbstractInterceptor.</code><code>class</code><code>);</code>

<code>    </code><code>//不需要攔截的連結</code>

<code>    </code><code>protected</code> <code>static</code> <code>List&lt;String&gt; excludeActionList = </code><code>new</code> <code>ArrayList&lt;String&gt;();</code>

<code>    </code><code>static</code> <code>{</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(login|static)(/)?(.+)?$"</code><code>);</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(flush|test|site_map)(/)?(.+)?$"</code><code>);     </code><code>//redis data flush</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/app/(flush|info.json|apkinfo.json)(/)?(.+)?$"</code><code>);    </code><code>//前端接口http://m.tv.sohu.com/app</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(hikeapp)(/)?(.+)?$"</code><code>);        </code><code>//需要拉起用戶端的專輯資料通路接口</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(cooperation|activity|api|open|mobile|mb)(/)?(.+)?$"</code><code>);</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(activity|api|open|mobile|mb)(/)?(.+)?$"</code><code>);</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(test)(/)?(.+)?$"</code><code>);</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>boolean</code> <code>preHandle(HttpServletRequest request,</code>

<code>            </code><code>HttpServletResponse response, Object handler) </code><code>throws</code> <code>Exception {</code>

<code>//      logger.debug(" ====== prehandle !");</code>

<code>//      logger.info(" ======= URI:{}",request.getRequestURI());</code>

<code>        </code><code>request.setAttribute(</code><code>"uri"</code><code>, request.getRequestURI());</code><code>//為了突出顯示選中的連結</code>

<code>        </code><code>for</code><code>(String excludeUrl : excludeActionList) {</code>

<code>            </code><code>if</code><code>(Pattern.matches(excludeUrl, request.getRequestURI())) {</code>

<code>                </code><code>return</code> <code>true</code><code>;</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

<code>        </code><code>return</code> <code>innerPreHandle(request, response, handler);</code>

<code>    </code><code>protected</code> <code>abstract</code> <code>boolean</code> <code>innerPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) </code><code>throws</code> <code>Exception;</code>

<code>    </code><code>public</code> <code>void</code> <code>postHandle(HttpServletRequest request,</code>

<code>            </code><code>HttpServletResponse response, Object handler,</code>

<code>            </code><code>ModelAndView modelAndView) </code><code>throws</code> <code>Exception {</code>

<code>//      logger.debug(" ====== postHandle !");</code>

<code>    </code><code>public</code> <code>void</code> <code>afterCompletion(HttpServletRequest request,</code>

<code>            </code><code>HttpServletResponse response, Object handler, Exception ex)</code>

<code>            </code><code>throws</code> <code>Exception {</code>

<code>//      logger.debug(" ====== afterCompletion !");</code>

<code>}</code>

   2.權限過濾

<code>import</code> <code>org.apache.commons.lang.StringUtils;</code>

<code>import</code> <code>org.springframework.beans.factory.annotation.Autowired;</code>

<code>import</code> <code>org.springframework.web.util.WebUtils;</code>

<code>import</code> <code>com.book.core.model.AdminFunctions;</code>

<code>import</code> <code>com.book.core.model.AdminRight;</code>

<code>import</code> <code>com.book.core.model.User;</code>

<code>import</code> <code>com.book.core.service.AdminFunctionsService;</code>

<code>import</code> <code>com.book.core.service.AdminRightService;</code>

<code>import</code> <code>com.book.core.utils.Constants;</code>

<code> </code><code>* 使用者權限過濾</code>

<code>public</code> <code>class</code> <code>FunctionsInterceptor </code><code>extends</code> <code>AbstractInterceptor{</code>

<code>    </code><code>private</code> <code>static</code> <code>Logger logger = LoggerFactory.getLogger(FunctionsInterceptor.</code><code>class</code><code>);</code>

<code>    </code><code>@Autowired</code>

<code>    </code><code>private</code> <code>AdminRightService adminRightService;</code>

<code>    </code><code>private</code> <code>AdminFunctionsService adminFunctionsService;</code>

<code>    </code><code>//登入後不需要攔截的連結</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(index|admin/user|admin/myinfo)(/)?(.+)?$"</code><code>);</code>

<code>        </code><code>excludeActionList.add(</code><code>"^/(book/del|book/detail.json|book/addorupdate)(/)?(.+)?$"</code><code>);</code>

<code>    </code><code>@Override</code>

<code>    </code><code>protected</code> <code>boolean</code> <code>innerPreHandle(HttpServletRequest request,</code>

<code>        </code><code>try</code> <code>{</code>

<code>            </code><code>User user = (User) WebUtils.getSessionAttribute(request, Constants.ADMIN_SESSION_USER_KEY);</code>

<code>            </code><code>if</code> <code>(user == </code><code>null</code><code>) {</code>

<code>                </code><code>response.sendRedirect(</code><code>"/login"</code><code>);</code>

<code>                </code><code>return</code> <code>false</code><code>;</code>

<code>            </code> 

<code>            </code><code>//查找該使用者的權限</code>

<code>            </code><code>AdminRight adminRight = adminRightService.getObjByUserName(user.getName());</code>

<code>            </code><code>if</code> <code>(adminRight == </code><code>null</code><code>) {</code>

<code>            </code><code>List&lt;AdminFunctions&gt; menus = </code><code>null</code><code>;</code>

<code>            </code><code>if</code> <code>(adminRight.getIsAdmin() == </code><code>1</code><code>) {</code>

<code>                </code><code>menus = adminFunctionsService.getAll(); </code>

<code>                </code> 

<code>                </code><code>request.setAttribute(</code><code>"menus"</code><code>, menus);</code>

<code>                </code><code>request.setAttribute(</code><code>"isAdmin"</code><code>, adminRight.getIsAdmin());</code>

<code>            </code><code>} </code><code>else</code> <code>{</code>

<code>                </code><code>String right = adminRight.getRights();</code>

<code>                </code><code>if</code> <code>(StringUtils.isNotBlank(right)) {</code>

<code>                    </code><code>String[] rs = right.split(</code><code>","</code><code>);</code>

<code>                    </code><code>List&lt;Integer&gt; listId = </code><code>null</code><code>;</code>

<code>                    </code> 

<code>                    </code><code>if</code> <code>(rs != </code><code>null</code> <code>&amp;&amp; rs.length &gt; </code><code>0</code><code>) {</code>

<code>                        </code><code>listId = </code><code>new</code> <code>ArrayList&lt;Integer&gt;();</code>

<code>                        </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; rs.length; i++) {</code>

<code>                            </code><code>if</code> <code>(StringUtils.isNotBlank(rs[i])) {</code>

<code>                                </code><code>listId.add(Integer.valueOf(rs[i]));</code>

<code>                            </code><code>}</code>

<code>                        </code><code>}</code>

<code>                    </code><code>}</code>

<code>                    </code><code>//查詢</code>

<code>                    </code><code>menus = adminFunctionsService.getObjByIds(listId);</code>

<code>                </code><code>}</code>

<code>                </code><code>for</code><code>(String excludeUrl : excludeActionList) {</code>

<code>                    </code><code>if</code><code>(Pattern.matches(excludeUrl, request.getRequestURI())) {</code>

<code>                        </code><code>request.setAttribute(</code><code>"menus"</code><code>, menus);</code>

<code>                        </code><code>request.setAttribute(</code><code>"isAdmin"</code><code>, adminRight.getIsAdmin());</code>

<code>                        </code><code>return</code> <code>true</code><code>;</code>

<code>                </code><code>//對權限進行過濾,不能輸入URL就可以通路</code>

<code>                </code><code>if</code> <code>(menus != </code><code>null</code> <code>&amp;&amp; menus.size() &gt; </code><code>0</code><code>) {</code>

<code>                    </code><code>for</code><code>(AdminFunctions adminFunctions : menus) {</code>

<code>                        </code><code>if</code> <code>(request.getRequestURI().startsWith(adminFunctions.getUrl())) {</code>

<code>                            </code><code>logger.info(</code><code>" ====== request.getRequestURI():{},table-url:{}"</code><code>,request.getRequestURI(),adminFunctions.getUrl());</code>

<code>                            </code><code>request.setAttribute(</code><code>"menus"</code><code>, menus);</code>

<code>                            </code><code>request.setAttribute(</code><code>"isAdmin"</code><code>, adminRight.getIsAdmin());</code>

<code>                            </code><code>return</code> <code>true</code><code>;</code>

<code>            </code><code>response.sendRedirect(</code><code>"/login"</code><code>);</code>

<code>            </code><code>return</code> <code>false</code><code>;</code>

<code>        </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

<code>            </code><code>logger.error(</code><code>" ====== get AdminRight error!"</code><code>,e);</code>

<code>            </code><code>e.printStackTrace();</code>

<code>        </code> 

<code>        </code><code>return</code> <code>false</code><code>;</code>

   3.登入驗證

<code>import</code> <code>java.net.URLEncoder;</code>

<code>import</code> <code>java.util.Calendar;</code>

<code>import</code> <code>java.util.Date;</code>

<code>import</code> <code>java.util.UUID;</code>

<code>import</code> <code>javax.servlet.http.Cookie;</code>

<code>import</code> <code>com.book.core.model.PersistentLogins;</code>

<code>import</code> <code>com.book.core.service.PersistentLoginsService;</code>

<code>import</code> <code>com.book.core.service.UserService;</code>

<code>import</code> <code>com.book.core.utils.CookieUtil;</code>

<code>import</code> <code>com.book.core.utils.EncryptionUtil;</code>

<code> </code><code>* 登入驗證攔截器</code>

<code>public</code> <code>class</code> <code>LoginInterceptor </code><code>extends</code> <code>AbstractInterceptor{</code>

<code>    </code><code>private</code> <code>static</code> <code>Logger logger = LoggerFactory.getLogger(LoginInterceptor.</code><code>class</code><code>);</code>

<code>    </code><code>private</code> <code>PersistentLoginsService persistentLoginsService;</code>

<code>    </code><code>private</code> <code>UserService userService;</code>

<code>        </code><code>User user = (User) WebUtils.getSessionAttribute(request, Constants.ADMIN_SESSION_USER_KEY);</code>

<code>        </code><code>if</code> <code>(user != </code><code>null</code><code>) {</code>

<code>            </code><code>//已登入</code>

<code>            </code><code>return</code> <code>true</code><code>;</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>            </code><code>//從cookie中取值</code>

<code>            </code><code>Cookie cookie = CookieUtil.getCookie(request, Constants.RememberMe_Admin);</code>

<code>            </code><code>if</code> <code>(cookie != </code><code>null</code><code>) {</code>

<code>                </code><code>String cookieValue = EncryptionUtil.base64Decode(cookie.getValue());</code>

<code>                </code><code>String[] cValues = cookieValue.split(</code><code>":"</code><code>);</code>

<code>                </code><code>if</code> <code>(cValues.length == </code><code>2</code><code>) {</code>

<code>                    </code><code>String userNameByCookie = cValues[</code><code>0</code><code>];</code><code>//擷取使用者名</code>

<code>                    </code><code>String uuidByCookie = cValues[</code><code>1</code><code>];</code><code>//擷取UUID值</code>

<code>                    </code><code>//到資料庫中查詢自動登入記錄</code>

<code>                    </code><code>PersistentLogins pLogins  = persistentLoginsService.getObjByUUID(uuidByCookie);</code>

<code>                    </code><code>if</code> <code>(pLogins != </code><code>null</code><code>) {</code>

<code>                        </code><code>String savedToken = pLogins.getToken();</code>

<code>                        </code> 

<code>                        </code><code>//擷取有效時間</code>

<code>                        </code><code>Date savedValidTime = pLogins.getValidTime();</code>

<code>                        </code><code>Date currentTime = </code><code>new</code> <code>Date();</code>

<code>                        </code><code>//如果還在有效期内,記錄判斷是否可以自動登入</code>

<code>                        </code><code>if</code> <code>(currentTime.before(savedValidTime)) {</code>

<code>                            </code><code>User u = userService.getUserByName(userNameByCookie);</code>

<code>                            </code><code>if</code> <code>(u != </code><code>null</code><code>) {</code>

<code>                                </code><code>Calendar calendar = Calendar.getInstance();</code>

<code>                                </code><code>calendar.setTime(savedValidTime);</code>

<code>                                </code> 

<code>                                </code><code>// 精确到分的時間字元串</code>

<code>                                </code><code>String timeString = calendar.get(Calendar.YEAR) + </code><code>"-"</code> <code>+ calendar.get(Calendar.MONTH)</code>

<code>                                        </code><code>+ </code><code>"-"</code> <code>+ calendar.get(Calendar.DAY_OF_MONTH) + </code><code>"-"</code>

<code>                                        </code><code>+ calendar.get(Calendar.HOUR_OF_DAY) + </code><code>"-"</code> <code>+ calendar.get(Calendar.MINUTE);</code>

<code>                                </code><code>// 為了校驗而生成的密文</code>

<code>                                </code><code>String newToken = EncryptionUtil.sha256Hex(u.getName() + </code><code>"_"</code> <code>+ u.getPassword() + </code><code>"_"</code>

<code>                                        </code><code>+ timeString + </code><code>"_"</code> <code>+ Constants.salt);</code>

<code>                                </code><code>// 校驗sha256加密的值,如果不一樣則表示使用者部分資訊已被修改,需要重新登入</code>

<code>                                </code><code>if</code> <code>(savedToken.equals(newToken)) {</code>

<code>                                    </code><code>//為了提高安全性,每次登入之後都更新自動登入的cookie值</code>

<code>                                    </code><code>String uuidNewString = UUID.randomUUID().toString();</code>

<code>                                    </code><code>String newCookieValue = EncryptionUtil.base64Encode(u.getName() + </code><code>":"</code> <code>+ uuidNewString);</code>

<code>                                    </code><code>CookieUtil.editCookie(request, response, Constants.RememberMe_Admin, newCookieValue, </code><code>null</code><code>);</code>

<code>                                    </code> 

<code>                                    </code><code>//同時更新資料</code>

<code>                                    </code><code>pLogins.setSeries(uuidNewString);</code>

<code>                                    </code><code>pLogins.setUpdateTime(</code><code>new</code> <code>Date());</code>

<code>                                    </code><code>persistentLoginsService.updateByObj(pLogins);</code>

<code>                                    </code><code>//将使用者加到session中,不退出浏覽器時隻需要判斷session即可</code>

<code>                                    </code><code>WebUtils.setSessionAttribute(request, Constants.ADMIN_SESSION_USER_KEY, u);</code>

<code>                                    </code><code>//校驗成功,此次攔截操作完成</code>

<code>                                    </code><code>return</code> <code>true</code><code>;</code>

<code>                                </code><code>} </code><code>else</code> <code>{</code>

<code>                                    </code><code>//使用者資訊部分被修改,删除cookie并清空資料庫中的記錄</code>

<code>                                    </code><code>CookieUtil.delCookie(response, cookie);</code>

<code>                                    </code><code>persistentLoginsService.delObjById(pLogins.getId());</code>

<code>                                </code><code>}</code>

<code>                        </code><code>} </code><code>else</code> <code>{</code>

<code>                            </code><code>// 超過儲存的有效期,删除cookie并清空資料庫中的記錄</code>

<code>                            </code><code>CookieUtil.delCookie(response, cookie);</code>

<code>                            </code><code>persistentLoginsService.delObjById(pLogins.getId());</code>

<code>            </code><code>try</code> <code>{</code>

<code>                </code><code>response.sendRedirect(</code><code>"/login?src="</code> <code>+ URLEncoder.encode(request.getRequestURI(), </code><code>"UTF-8"</code><code>));</code>

<code>            </code><code>} </code><code>catch</code> <code>(Exception e) {</code>

<code>                </code><code>logger.error(</code><code>" ===== loginInterceptor error ,url:{}{}"</code><code>,request.getRequestURL(),request.getRequestURI(),e);</code>

<code>            </code><code>} </code>

   4.spring-mvc-servlet.xml的配置

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"UTF-8"</code><code>?&gt;</code>

<code>&lt;</code><code>beans</code> <code>xmlns</code><code>=</code><code>"http://www.springframework.org/schema/beans"</code>  

<code>    </code><code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/XMLSchema-instance"</code> 

<code>    </code><code>xmlns:mvc</code><code>=</code><code>"http://www.springframework.org/schema/mvc"</code> 

<code>    </code><code>xmlns:context</code><code>=</code><code>"http://www.springframework.org/schema/context"</code> 

<code>    </code><code>xsi:schemaLocation=" </code>

<code>        </code><code>http://www.springframework.org/schema/beans  </code>

<code>        </code><code>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd </code>

<code>        </code><code>http://www.springframework.org/schema/context  </code>

<code>        </code><code>http://www.springframework.org/schema/context/spring-context-3.0.xsd </code>

<code>        </code><code>http://www.springframework.org/schema/mvc </code>

<code>        </code><code>http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"&gt;</code>

<code>    </code><code>&lt;!-- 指定一個包讓其自動掃描 --&gt;</code>

<code>    </code><code>&lt;</code><code>context:component-scan</code> <code>base-package</code><code>=</code><code>"com.book.admin.controller"</code><code>/&gt;</code>

<code>    </code><code>&lt;</code><code>mvc:annotation-driven</code><code>/&gt;</code>

<code>    </code><code>&lt;!-- 下面注釋的部分表示:強調所有的請求都要經過springmvc架構 --&gt;</code>

<code>    </code><code>&lt;</code><code>mvc:default-servlet-handler</code><code>/&gt;</code>

<code>    </code><code>&lt;!-- 放行了以/static/開始的請求 --&gt;</code>

<code>    </code><code>&lt;</code><code>mvc:resources</code> <code>location</code><code>=</code><code>"/static/"</code> <code>mapping</code><code>=</code><code>"/static/**"</code><code>/&gt; </code>

<code>    </code><code>&lt;!-- 當一個方法完全是為了跳轉時,我們可以省略該方法,而在此寫一個配置就行了</code>

<code>    </code><code>&lt;mvc:view-controller path="/index" view-name="index"/&gt;</code>

<code>    </code><code>&lt;mvc:view-controller path="/main" view-name="main"/&gt;</code>

<code>    </code><code>&lt;mvc:view-controller path="/success" view-name="success"/&gt; </code>

<code>    </code><code>&lt;mvc:view-controller path="/index" view-name="main"/&gt;</code>

<code>    </code><code>&lt;mvc:view-controller path="/" view-name="main"/&gt; </code>

<code>    </code><code>&lt;mvc:view-controller path="/admin/myinfo" view-name="myinfo"/&gt;--&gt;</code>

<code>    </code><code>&lt;!-- </code>

<code>    </code><code>&lt;mvc:view-controller path="/book" view-name="book"/&gt;</code>

<code>     </code><code>--&gt;</code>

<code>    </code><code>&lt;</code><code>mvc:interceptors</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>bean</code> <code>class</code><code>=</code><code>"com.book.admin.interceptor.LoginInterceptor"</code><code>&gt;&lt;/</code><code>bean</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>bean</code> <code>class</code><code>=</code><code>"com.book.admin.interceptor.FunctionsInterceptor"</code><code>&gt;&lt;/</code><code>bean</code><code>&gt;</code>

<code>        </code><code>&lt;!--  </code>

<code>        </code><code>&lt;mvc:interceptor&gt;  </code>

<code>            </code><code>&lt;mvc:mapping path="/test/number.do"/&gt;  </code>

<code>            </code><code>&lt;bean class="com.host.app.web.interceptor.LoginInterceptor"/&gt;  </code>

<code>        </code><code>&lt;/mvc:interceptor&gt;  </code>

<code>        </code><code>--&gt;</code>

<code>    </code><code>&lt;/</code><code>mvc:interceptors</code><code>&gt;</code>

<code>    </code><code>&lt;!-- 配置springmvc的視圖解析器 --&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"viewResolver"</code> 

<code>        </code><code>class</code><code>=</code><code>"org.springframework.web.servlet.view.InternalResourceViewResolver"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"suffix"</code> <code>value</code><code>=</code><code>".jsp"</code><code>/&gt;</code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"prefix"</code> <code>value</code><code>=</code><code>"/WEB-INF/views/"</code><code>/&gt;</code>

<code>    </code><code>&lt;/</code><code>bean</code><code>&gt;</code>

<code>    </code><code>&lt;!-- 檔案上傳解析器   --&gt;</code>

<code>    </code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"multipartResolver"</code> 

<code>        </code><code>class</code><code>=</code><code>"org.springframework.web.multipart.commons.CommonsMultipartResolver"</code><code>&gt; </code>

<code>        </code><code>&lt;</code><code>property</code> <code>name</code><code>=</code><code>"maxUploadSize"</code> <code>value</code><code>=</code><code>"100000"</code><code>/&gt; </code>

<code>&lt;/</code><code>beans</code><code>&gt;</code>

參考:

攔截器的基礎了解

<a href="http://blog.csdn.net/sunp823/article/details/51694662">http://blog.csdn.net/sunp823/article/details/51694662</a>

攔截器的詳細了解

<a href="http://jinnianshilongnian.iteye.com/blog/1670856">http://jinnianshilongnian.iteye.com/blog/1670856</a>

登入例子的思路了解

<a href="http://blog.51cto.com/983836259/1880284">http://blog.51cto.com/983836259/1880284</a>

正規表達式

<a href="http://www.cnblogs.com/sparkbj/articles/6207103.html">http://www.cnblogs.com/sparkbj/articles/6207103.html</a>

springmvc的攔截器和過濾器的差別

<a href="http://blog.csdn.net/xiaoyaotan_111/article/details/53817918">http://blog.csdn.net/xiaoyaotan_111/article/details/53817918</a>

java Web中的過濾器Filter和interceptor的了解

<a href="http://www.jianshu.com/p/39c0cfe25997">http://www.jianshu.com/p/39c0cfe25997</a>

     本文轉自韓立偉 51CTO部落格,原文連結:http://blog.51cto.com/hanchaohan/2047966,如需轉載請自行聯系原作者