SSM與SpringBoot整合shiro

1、SSM整合shiro

1.1 添加依賴(lài)

和之前的SSM相比速缨,整合shiro的時(shí)候需要添加一個(gè)依賴(lài)

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

shiro-spring這個(gè)依賴(lài)包含了shiro-core與shiro-web尤慰,添加之后就不再需要添加shiro-core和shiro-web了关斜。

1.2 spring-shiro.xml配置

spring-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置shiro的過(guò)濾器工廠-->
    <bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
        <!--設(shè)置securityManager-->
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/index.jsp"/>
        <!-- override these for application-specific URLs if you like:
        <property name="loginUrl" value="/login.jsp"/>
        <property name="successUrl" value="/home.jsp"/>
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
        <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean  -->
        <!-- defined will be automatically acquired and available via its beanName in chain        -->
        <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
        <!-- <property name="filters">
            <util:map>
                <entry key="anAlias" value-ref="someFilter"/>
            </util:map>
        </property> -->
        <!--配置url與權(quán)限-->
        <property name="filterChainDefinitions">
            <value>
                /index.jsp=anon
                /main.jsp=authc
                /manager.jsp=roles[manager]
                /guest.jsp=roles[guest]
            </value>
        </property>
    </bean>
    <!--配置默認(rèn)的webSecurityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
        <!--自定義自己的realm-->
        <property name="realm" ref="myRealm"/>
        <!-- By default the servlet container sessions will be used.  Uncomment this line
             to use shiro's native sessions (see the JavaDoc for more): -->
        <!-- <property name="sessionMode" value="native"/> -->
    </bean>
    <bean id="myRealm" class="com.qianfeng.shiro.MyRealm"/>
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" id="lifecycleBeanPostProcessor"/>
</beans>

這個(gè)xml文件配置了四個(gè)bean:ShiroFilterFactoryBean叽粹、DefaultWebSecurityManager炭菌、MyRealm跃须、LifecycleBeanPostProcessor。ShiroFilterFactoryBean的作用是配置過(guò)濾器工廠娃兽,id要和稍后要說(shuō)的web.xml中的shiro過(guò)濾器的名稱(chēng)一樣,指定securityManager尽楔,等于頁(yè)面投储,過(guò)濾頁(yè)面。DefaultWebSecurityManager這個(gè)的主要作用就是配置realm阔馋,如果有多個(gè)realm就要配置realms玛荞,MyRealm是我們自定義的Realm,然后LifecycleBeanPostProcessor是一個(gè)AOP的實(shí)現(xiàn)呕寝。

1.3 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--配置過(guò)濾器勋眯,這個(gè)過(guò)濾器的名字應(yīng)該和spring-shiro.xml中過(guò)濾器的id一樣-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!--
            將過(guò)濾器的生命周期從出生到死亡完全交給Spring來(lái)管理
        -->
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
    <!-- requests.  Usually this filter mapping is defined first (before all others) to -->
    <!-- ensure that Shiro works in subsequent filters in the filter chain:             -->
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--    spring寫(xiě)好的中文過(guò)濾器-->
    <filter>
        <filter-name>encode</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encode</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

和shiro相關(guān)的就是一個(gè)過(guò)濾器和一個(gè)監(jiān)聽(tīng)器,這個(gè)過(guò)濾器類(lèi)是DelegatingFilterProxy下梢,name應(yīng)該和spring-shiro.xml中ShiroFilterFactoryBean的id一樣客蹋,監(jiān)聽(tīng)器是ContextLoaderListener,文本加載監(jiān)聽(tīng)器孽江,context-param這個(gè)標(biāo)簽將classpath目錄下的所有xml文件讀取進(jìn)來(lái)讶坯。

1.4 MyRealm

MyReanl.java

package com.qianfeng.shiro;

import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author huwen
 */
public class MyRealm extends AuthorizingRealm {

    @Resource
    private IEmployeeService employeeService;
    /**
     * 授權(quán)方法
     * @param principalCollection principal的集合,可以理解為各種用戶(hù)身份的集合岗屏,比如用戶(hù)名辆琅、郵箱、手機(jī)號(hào)等
     * @return 返回的是授權(quán)信息这刷,包括角色與權(quán)限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //SimpleAuthorizationInfo是AuthorizationInfo的子類(lèi)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //得到可用的principal婉烟,然后轉(zhuǎn)換為字符串
        String empName = getAvailablePrincipal(principalCollection).toString();
        //調(diào)用service
        List<Roles> roles = employeeService.getAllRolesByEmpName(empName);
        for (Roles role : roles) {
            info.addRole(role.getRoleName());
        }
        List<Permission> permissions = employeeService.getAllPermissionsByEmpName(empName);
        for (Permission permission : permissions) {
            info.addStringPermission(permission.getPermName());
        }
        return info;
    }

    /**
     * 這個(gè)方法用于認(rèn)證
     * @param authenticationToken 用戶(hù)名與密碼
     * @return 認(rèn)證信息
     * @throws AuthenticationException 可能引發(fā)的異常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        AuthenticationInfo info = null;
        //將authenticationToken強(qiáng)轉(zhuǎn)為usernamePasswordToken,向下轉(zhuǎn)型能夠成功因?yàn)槲覀冎烙脩?hù)輸入的是用戶(hù)名與密碼
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //從token中獲取用戶(hù)名與密碼傳給service暇屋,最后交給dao從數(shù)據(jù)庫(kù)中查詢(xún)
        String username = token.getUsername();
        //使用字節(jié)數(shù)組存儲(chǔ)密碼更為安全似袁,因?yàn)樽止?jié)數(shù)組是可變的,字符串是不可變的存在常量池中
        char[] password = token.getPassword();
        String pass = new String(password);
        Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
        //如果查詢(xún)到的數(shù)據(jù)不為空率碾,就構(gòu)造一個(gè)SimpleAuthenticationInfo對(duì)象叔营,將用戶(hù)名與密碼放在里里面
        if(emp!=null && emp.getEmpId()!=0){
            //getName()獲取到的是當(dāng)前Realm的標(biāo)識(shí),因?yàn)榭梢宰远x多個(gè)Realm所宰,不同的Realm需要區(qū)分
            info = new SimpleAuthenticationInfo(username,pass,getName());
        }
        return info;
    }
}

MyRealm繼承自AuthorizingRealm绒尊,重寫(xiě)兩個(gè)方法,第一個(gè)方法用于授權(quán)仔粥,第二個(gè)方法用于認(rèn)證婴谱。

1.5 EmployeeController

package com.qianfeng.controller;

import com.qianfeng.entity.Employee;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

import javax.annotation.Resource;

@Controller
public class EmployeeController {
    @Resource
    private IEmployeeService employeeService;
    @PostMapping("/login")
    public String empLogin(Employee employee){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(employee.getEmpName(),employee.getPassword());
        try {
            subject.login(token);
            return "main";
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return "index";
    }
}

可以看到和之前的web項(xiàng)目的不同蟹但,不需要我們手動(dòng)創(chuàng)建配置工廠生成實(shí)例了。

2谭羔、SpringBoot整合shiro

2.1 添加依賴(lài)

這里shiro需要的依賴(lài)和SSM項(xiàng)目中的依賴(lài)一樣华糖,都是shiro-spring。如果想要在thymeleaf中使用shiro標(biāo)簽瘟裸,需要添加下面的依賴(lài):

 <dependency>
     <groupId>com.github.theborakompanioni</groupId>
     <artifactId>thymeleaf-extras-shiro</artifactId>
     <version>2.0.0</version>
</dependency>

然后載html標(biāo)簽中添加xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"客叉。

2.2 MyRealm

package com.qianfeng.shiro;

import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author huwen
 */
@Component("myRealm")
public class MyRealm extends AuthorizingRealm {

    @Resource
    private IEmployeeService employeeService;
    /**
     * 授權(quán)方法
     * @param principalCollection principal的集合,可以理解為各種用戶(hù)身份的集合话告,比如用戶(hù)名兼搏、郵箱、手機(jī)號(hào)等
     * @return 返回的是授權(quán)信息沙郭,包括角色與權(quán)限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //SimpleAuthorizationInfo是AuthorizationInfo的子類(lèi)
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //得到可用的principal佛呻,然后轉(zhuǎn)換為字符串
        String empName = getAvailablePrincipal(principalCollection).toString();
        //調(diào)用service
        List<Roles> roles = employeeService.getAllRolesByEmpName(empName);
        for (Roles role : roles) {
            info.addRole(role.getRoleName());
        }
        List<Permission> permissions = employeeService.getAllPermissionsByEmpName(empName);
        for (Permission permission : permissions) {
            info.addStringPermission(permission.getPermName());
        }
        return info;
    }

    /**
     * 這個(gè)方法用于認(rèn)證
     * @param authenticationToken 用戶(hù)名與密碼
     * @return 認(rèn)證信息
     * @throws AuthenticationException 可能引發(fā)的異常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        AuthenticationInfo info = null;
        //將authenticationToken強(qiáng)轉(zhuǎn)為usernamePasswordToken,向下轉(zhuǎn)型能夠成功因?yàn)槲覀冎烙脩?hù)輸入的是用戶(hù)名與密碼
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //從token中獲取用戶(hù)名與密碼傳給service病线,最后交給dao從數(shù)據(jù)庫(kù)中查詢(xún)
        String username = token.getUsername();
        //使用字節(jié)數(shù)組存儲(chǔ)密碼更為安全吓著,因?yàn)樽止?jié)數(shù)組是可變的,字符串是不可變的存在常量池中
        char[] password = token.getPassword();
        String pass = new String(password);
        Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
        //如果查詢(xún)到的數(shù)據(jù)不為空送挑,就構(gòu)造一個(gè)SimpleAuthenticationInfo對(duì)象绑莺,將用戶(hù)名與密碼放在里里面
        if(emp!=null && emp.getEmpId()!=0){
            //getName()獲取到的是當(dāng)前Realm的標(biāo)識(shí),因?yàn)榭梢宰远x多個(gè)Realm惕耕,不同的Realm需要區(qū)分
            info = new SimpleAuthenticationInfo(username,pass,getName());
        }
        return info;
    }
}

MyRealm這個(gè)類(lèi)和SSM項(xiàng)目基本一樣紊撕,唯一的不同是加上了@Component("myRealm")這個(gè)注解

2.3 ShiroConfig

ShiroConfig.java

package com.qianfeng.config;

import com.qianfeng.shiro.MyRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    /**
     * 該方法用于返回過(guò)濾器工廠
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,String> map = new HashMap<>(10);
        map.put("/index.html","anon");
        map.put("/main.html","authc");
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauth.html");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    /**
     * 設(shè)置realm為自定義的Realm,返回securityManager
     * @param realm
     * @return
     */
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm")MyRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 開(kāi)啟shiro的注解赡突,需要借助SpringAOP掃描Shiro注解的類(lèi)对扶,來(lái)進(jìn)行安全校驗(yàn)
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 開(kāi)啟aop的注解支持
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);

        return  authorizationAttributeSourceAdvisor;
    }
}

由于springboot不使用xml文件進(jìn)行配置,所以采用Java類(lèi)的方式對(duì)shiro進(jìn)行設(shè)置惭缰,整個(gè)類(lèi)上有一個(gè)注解@Configuration浪南,共有四個(gè)方法每個(gè)方法都有一個(gè)@Bean注解,方法名可以自定義漱受,但是參數(shù)與返回值必須對(duì)應(yīng)络凿,第一個(gè)方法的作用是設(shè)置并返回過(guò)濾器工廠,第二個(gè)方法的作用是設(shè)置securityManager并返回昂羡,下面那兩個(gè)方法都是和AOP相關(guān)的絮记。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市虐先,隨后出現(xiàn)的幾起案子怨愤,更是在濱河造成了極大的恐慌,老刑警劉巖蛹批,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撰洗,死亡現(xiàn)場(chǎng)離奇詭異篮愉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)差导,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)试躏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人设褐,你說(shuō)我怎么就攤上這事颠蕴。” “怎么了助析?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵裁替,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我貌笨,道長(zhǎng),這世上最難降的妖魔是什么襟沮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任锥惋,我火速辦了婚禮,結(jié)果婚禮上开伏,老公的妹妹穿的比我還像新娘膀跌。我一直安慰自己,他們只是感情好固灵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布捅伤。 她就那樣靜靜地躺著,像睡著了一般巫玻。 火紅的嫁衣襯著肌膚如雪丛忆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天仍秤,我揣著相機(jī)與錄音熄诡,去河邊找鬼。 笑死诗力,一個(gè)胖子當(dāng)著我的面吹牛凰浮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苇本,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼袜茧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了瓣窄?” 一聲冷哼從身側(cè)響起笛厦,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俺夕,沒(méi)想到半個(gè)月后递递,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體喷橙,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年登舞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贰逾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菠秒,死狀恐怖疙剑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情践叠,我是刑警寧澤言缤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站禁灼,受9級(jí)特大地震影響管挟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弄捕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一僻孝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧守谓,春花似錦穿铆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至平酿,卻和暖如春凤优,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜈彼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工别洪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柳刮。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓挖垛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秉颗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痢毒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容