Shiro框架學(xué)習(xí)筆記

doGetAuthorizationInfo和doGetAuthenticationInfo

????這兩個(gè)方法雖然名字很像地啰,但是意義是不一樣的碍彭,doGetAuthorizationInfo方法是進(jìn)行權(quán)限驗(yàn)證透绩,doGetAuthenticationInfo是進(jìn)行身份驗(yàn)證的(登錄驗(yàn)證)


Realm就是負(fù)責(zé)驗(yàn)證的東西,可以是HashMap,SimpleAccountRealm,ini文件,數(shù)據(jù)庫等 最常用的還是數(shù)據(jù)庫.

簡單驗(yàn)證

1.構(gòu)建SecurityManager環(huán)境

? ??DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();

2.為SecurityManager設(shè)置檢測源

defaultSecurityManager.setRealm(simpleAccountRealm);

3.設(shè)置SecurityManager

SecurityUtils.setSecurityManager(defaultSecurityManager);

4.獲得用戶主體

Subject subject=SecurityUtils.getSubject();

5.根據(jù)用戶密碼獲得token

UsernamePasswordToken token=new UsernamePasswordToken("Kenny","123456");

6.根據(jù)登陸

subject.login(token);

7.檢測是否登陸成功和權(quán)限問題

subject.isAuthenticated()? ? return boolean

subject.checkRole("admin");檢測角色

subject.checkPermission("user:update");檢測權(quán)限

通過INI文件驗(yàn)證

IniRealm iniRealm=new IniRealm("classpath:user.ini");

ini文件內(nèi)容如下:

[users]

Kenny=111,admin

[roles]

admin=user:delete,user:update

剩下的和之前一樣

通過數(shù)據(jù)庫驗(yàn)證

1.初始化JdbcRealm

JdbcRealm jdbcRealm=new JdbcRealm();

2.為JdbcRealm設(shè)置數(shù)據(jù)源

jdbcRealm.setDataSource(druidDataSource);

3.設(shè)置自定義sql語句查詢是否有該用戶

String sql="select password from test_user where username=?";

jdbcRealm.setAuthenticationQuery(sql);

4.設(shè)置自定義sql語句查詢是否有該角色

String sql="select password from test_user where username=?";

String roleSql="select role_name from test_user_role where user_name=?";

jdbcRealm.setUserRolesQuery(roleSql);

后面都一致


Shiro框架結(jié)合Springboot2.X

1.在pom.xml文件中導(dǎo)入必要的依賴

spring-boot-starter-web

spring-boot-starter-test

spring-boot-starter-thymeleaf

shiro-core

shiro-spring

mysql-connector-java

druid(阿里巴巴的)

下面兩個(gè)是打開aop,實(shí)現(xiàn)注解管理權(quán)限功能

spring-boot-starter-aop

aspectjweaver

redis所需要的依賴(在日常開發(fā)問題文章中有寫)

fastjson


2.設(shè)置自己的Realm

package com.kenny.shiroweb.realm;

import com.kenny.shiroweb.dao.UserDao;

import com.kenny.shiroweb.dao.UserRoleDao;

import com.kenny.shiroweb.vo.User;

import com.kenny.shiroweb.vo.UserRole;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.SimpleAuthenticationInfo;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.crypto.hash.Md5Hash;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.apache.shiro.util.ByteSource;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.util.*;

@Component

public class CustomRealmextends AuthorizingRealm {

MapuserMap=new HashMap(16);

? ? {

userMap.put("Kenny","672332263137c302ab54e66efd791c1b");

? ? ? ? super.setName("customRealm");

? ? }

@Autowired

? ? private UserDaouserDao;

? ? @Autowired

? ? private UserRoleDaoroleDao;

? ? protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection) {

String userName= (String) principalCollection.getPrimaryPrincipal();

? ? ? ? //從數(shù)據(jù)庫或者緩存中獲取角色數(shù)據(jù)

? ? ? ? Set roles=getRolesByUserName(userName);

? ? ? ? Set permissions=getPermissionsByUserName(userName);

? ? ? ? SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();

? ? ? ? simpleAuthorizationInfo.setRoles(roles);

? ? ? ? simpleAuthorizationInfo.setStringPermissions(permissions);

? ? ? ? return simpleAuthorizationInfo;

? ? }

protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {

//1.從主體傳來得認(rèn)證信息中,獲得用戶名

? ? ? ? String userName=(String)authenticationToken.getPrincipal();

? ? ? ? //2.通過用戶名到數(shù)據(jù)庫中獲取憑證

? ? ? ? String password=getPassworkByUserName(userName);

? ? ? ? if(password==null){

return null;

? ? ? ? }

SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(userName,password,"customRealm");

? ? ? ? authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));

? ? ? ? return authenticationInfo;

? ? }

private SetgetPermissionsByUserName(String userName) {

Set sets=new HashSet();

? ? ? ? sets.add("user:delete");

? ? ? ? sets.add("user:add");

? ? ? ? return sets;

? ? }

private SetgetRolesByUserName(String userName) {

System.out.println("從數(shù)據(jù)庫中獲取授權(quán)數(shù)據(jù)");

? ? ? ? List list=roleDao.findByUsername(userName);

? ? ? ? List role=new ArrayList<>();

? ? ? ? for(UserRole userRole:list){

role.add(userRole.getRolename());

? ? ? ? }

Set sets=new HashSet<>(role);

? ? ? ? return sets;

? ? }

private StringgetPassworkByUserName(String userName) {

List user=userDao.findByUsername(userName);

? ? ? ? if(user!=null){

return user.get(0).getPassword();

? ? ? ? }

return null;

? ? }

public static void main(String[] args){

Md5Hash md5Hash=new Md5Hash("123","viewer");

? ? ? ? System.out.println(md5Hash.toString());

? ? }

}

密碼加密方式是通過md5加鹽(用戶名)的形式進(jìn)行加密


2.進(jìn)行shiro配置


package com.kenny.shiroweb.config;

import com.kenny.shiroweb.cache.RedisCacheManager;

import com.kenny.shiroweb.filter.RolesOrFilter;

import com.kenny.shiroweb.realm.CustomRealm;

import com.kenny.shiroweb.session.CustomSessionManger;

import com.kenny.shiroweb.session.RedisSessionDao;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

import org.apache.shiro.codec.Base64;

import org.apache.shiro.mgt.SecurityManager;

import org.apache.shiro.session.mgt.SessionManager;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;

import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.CookieRememberMeManager;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.apache.shiro.web.servlet.SimpleCookie;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;

import java.util.LinkedHashMap;

import java.util.Map;

@Configuration

public class ShiroConfig {

@Bean

? ? public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager) {

System.out.println("ShiroConfiguration.shirFilter()");

? ? ? ? ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();

? ? ? ? shiroFilterFactoryBean.setSecurityManager(securityManager);

? ? ? ? //自定義攔截器

? ? ? ? Map filtersMap =new LinkedHashMap();

? ? ? ? filtersMap.put("rolesOrFilter", new RolesOrFilter());

? ? ? ? shiroFilterFactoryBean.setFilters(filtersMap);

? ? ? ? //攔截器.

? ? ? ? Map filterChainDefinitionMap =new LinkedHashMap();

? ? ? ? // 配置不會(huì)被攔截的鏈接 順序判斷

? ? ? ? filterChainDefinitionMap.put("/static/**", "anon");

? ? ? ? filterChainDefinitionMap.put("/subLogin", "anon");

? ? ? ? filterChainDefinitionMap.put("/isLogin", "anon");

? ? ? ? //測試權(quán)限訪問

? ? ? ? filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");

? ? ? ? //配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了

? ? ? ? filterChainDefinitionMap.put("/logout.html", "logout");

? ? ? ? //:這是一個(gè)坑呢启妹,一不小心代碼就不好使了;

//

? ? ? ? filterChainDefinitionMap.put("/**", "authc");

? ? ? ? // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.html"頁面

? ? ? ? shiroFilterFactoryBean.setLoginUrl("/login.html");

? ? ? ? // 登錄成功后要跳轉(zhuǎn)的鏈接

? ? ? ? shiroFilterFactoryBean.setSuccessUrl("/index.html");

? ? ? ? //未授權(quán)界面;

? ? ? ? shiroFilterFactoryBean.setUnauthorizedUrl("/403");

? ? ? ? shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

? ? ? ? return shiroFilterFactoryBean;

? ? }

//選擇加密方式

? ? @Bean

? ? public HashedCredentialsMatcherhashedCredentialsMatcher(){

HashedCredentialsMatcher hashedCredentialsMatcher =new HashedCredentialsMatcher();

? ? ? ? hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;

? ? ? ? hashedCredentialsMatcher.setHashIterations(1);//散列的次數(shù),比如散列兩次框沟,相當(dāng)于 md5(md5(""));

? ? ? ? return hashedCredentialsMatcher;

? ? }

//將自定義的reamlm注冊為bean,并通過hashedCredentialsMatcher進(jìn)行加密

? ? @Bean

? ? public CustomRealmcustomRealm(){

CustomRealm customRealmRealm =new CustomRealm();

? ? ? ? customRealmRealm.setCredentialsMatcher(hashedCredentialsMatcher());

? ? ? ? return customRealmRealm;

? ? }

/**

* 項(xiàng)目自定義的DAO

*/

? ? @Bean

? ? public RedisSessionDaoredisSessionDao() {

return new RedisSessionDao();

? ? }

/**

* 自定義的CacheManager

? ? * @return

? ? */

? ? @Bean

? ? public RedisCacheManagerredisCacheManager() {

return new RedisCacheManager();

? ? }

//自定義sessionmanager

? ? @Bean("sessionManager")

public CustomSessionMangersessionManager() {

CustomSessionManger sessionManager =new CustomSessionManger();

? ? ? ? sessionManager.setSessionDAO(redisSessionDao());

? ? ? ? return sessionManager;

? ? }

/**

* 設(shè)置記住我的cookie

? ? * @return

? ? */

? ? @Bean

? ? public SimpleCookierememberMeCookie(){

//System.out.println("ShiroConfiguration.rememberMeCookie()");

//這個(gè)參數(shù)是cookie的名稱牍氛,對應(yīng)前端的checkbox的name = rememberMe

? ? ? ? SimpleCookie simpleCookie =new SimpleCookie("rememberMe");

? ? ? ? //

? ? ? ? simpleCookie.setMaxAge(259200);

? ? ? ? return simpleCookie;

? ? }

/**

* cookie管理對象;

* rememberMeManager()方法是生成rememberMe管理器晨继,而且要將這個(gè)rememberMe管理器設(shè)置到securityManager中

? ? * @return

? ? */

? ? @Bean

? ? public CookieRememberMeManagerrememberMeManager(){

//System.out.println("ShiroConfiguration.rememberMeManager()");

? ? ? ? CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();

? ? ? ? cookieRememberMeManager.setCookie(rememberMeCookie());

? ? ? ? //rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)

? ? ? ? cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));

? ? ? ? return cookieRememberMeManager;

? ? }

@Bean

? ? public DefaultWebSecurityManagersecurityManager(){

DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();

? ? ? ? securityManager.setRealm(customRealm());

? ? ? ? securityManager.setSessionManager(sessionManager());

? ? ? ? securityManager.setCacheManager(redisCacheManager());

? ? ? ? securityManager.setRememberMeManager(rememberMeManager());

? ? ? ? return securityManager;

? ? }

/**

* Shiro生命周期處理器

? ? * @return

? ? */

? ? @Bean

? ? public LifecycleBeanPostProcessorlifecycleBeanPostProcessor(){

return new LifecycleBeanPostProcessor();

? ? }

/**

* 開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類,并在必要時(shí)進(jìn)行安全邏輯驗(yàn)證

* 配置以下兩個(gè)bean(DefaultAdvisorAutoProxyCreator(可選)和AuthorizationAttributeSourceAdvisor)即可實(shí)現(xiàn)此功能

? ? * @return

? ? */

? ? @Bean

? ? @DependsOn({"lifecycleBeanPostProcessor"})

public DefaultAdvisorAutoProxyCreatoradvisorAutoProxyCreator(){

DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =new DefaultAdvisorAutoProxyCreator();

? ? ? ? advisorAutoProxyCreator.setProxyTargetClass(true);

? ? ? ? return advisorAutoProxyCreator;

? ? }

@Bean

? ? public AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor() {

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =new AuthorizationAttributeSourceAdvisor();

? ? ? ? authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());

? ? ? ? return authorizationAttributeSourceAdvisor;

? ? }

}

還有redis的配置文件不在這寫了


通過注解在控制層進(jìn)行攔截

//必須要admin角色才能訪問

@RequiresRoles("admin")?

//要求subject中必須同時(shí)含有file:read和write:aFile.txt的權(quán)限才能執(zhí)行方法

@RequiresPermissions({"user:delete", "user:add"} )


自定義攔截器

創(chuàng)建一個(gè)類繼承AuthorizationFilter并重新他的isAccessAllowed方法,然后在ShiroConfig文件中配置

//自定義攔截器

Map filtersMap =new LinkedHashMap();

filtersMap.put("rolesOrFilter", new RolesOrFilter());

shiroFilterFactoryBean.setFilters(filtersMap);


自定義攔截器代碼

package com.kenny.shiroweb.filter;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

public class RolesOrFilterextends AuthorizationFilter {

@Override

? ? protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)throws Exception {

Subject subject=getSubject(servletRequest,servletResponse);

? ? ? ? String[] roles=(String[])o;

? ? ? ? if(roles==null||roles.length==0){

return true;

? ? ? ? }

for (String role : roles){

if(subject.hasRole(role)){

return true;

? ? ? ? ? ? }

}

return false;

? ? }

}

在ShiroConfig中使用:

filterChainDefinitionMap.put("/testConfigRole", "rolesOrFilter[user,admin]");


自定義SessionManger

在ShiroConfig中注冊為bean

package com.kenny.shiroweb.session;

import org.apache.shiro.session.Session;

import org.apache.shiro.session.UnknownSessionException;

import org.apache.shiro.session.mgt.SessionKey;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;

import org.apache.shiro.web.session.mgt.WebSessionKey;

import javax.servlet.ServletRequest;

import java.io.Serializable;

public class CustomSessionMangerextends DefaultWebSessionManager {

@Override

? ? protected SessionretrieveSession(SessionKey sessionKey)throws UnknownSessionException {

Serializable sessionId=getSessionId(sessionKey);

? ? ? ? ServletRequest request=null;

? ? ? ? if(sessionKeyinstanceof WebSessionKey){

request=((WebSessionKey)sessionKey).getServletRequest();

? ? ? ? }

if(request!=null&&sessionId!=null){

Session session=(Session)request.getAttribute(sessionId.toString());

? ? ? ? ? ? if(session!=null){

return session;

? ? ? ? ? ? }

}

Session session=super.retrieveSession(sessionKey);

? ? ? ? if(request!=null&&sessionId!=null){

request.setAttribute(sessionId.toString(),session);

? ? ? ? }

return session;

? ? }

}?


自定義SessionDao

新建一個(gè)類繼承AbstractSessionDAO并重寫他的方法

主要還是增刪改查


key以kenny-session:為前綴加上sessionid

value:為session的序列化

因?yàn)閞edis中不能直接存儲(chǔ)對象,需要將他序列化SerializationUtils.serialize(session);

當(dāng)需要從redis中取出來的時(shí)候再反序列化強(qiáng)轉(zhuǎn)為Session對象即可.

然后將自定義的SessionDao注冊為Bean,

在剛自定義的CustomSessionManger添加自定義的DAO

sessionManager.setSessionDAO(redisSessionDao());


自定義CacheManager

新建一個(gè)類并實(shí)現(xiàn)CacheManager接口

還是通過redis進(jìn)行緩存,與SessionManager類似,只是存儲(chǔ)的對象不一樣

將剛自定義的redisCacheManager在ShiroConfig中注冊為bean


記住我功能

在ShrioConfig文件中添加兩個(gè)Bean

? ??/**

* 設(shè)置記住我的cookie

* @return

*/

@Bean

public SimpleCookierememberMeCookie(){

//System.out.println("ShiroConfiguration.rememberMeCookie()");

//這個(gè)參數(shù)是cookie的名稱,對應(yīng)前端的checkbox的name = rememberMe

? ? SimpleCookie simpleCookie =new SimpleCookie("rememberMe");

? ? //

? ? simpleCookie.setMaxAge(259200);

? ? return simpleCookie;

}

/**

* cookie管理對象;

* rememberMeManager()方法是生成rememberMe管理器搬俊,而且要將這個(gè)rememberMe管理器設(shè)置到securityManager中

* @return

*/

@Bean

public CookieRememberMeManagerrememberMeManager(){

//System.out.println("ShiroConfiguration.rememberMeManager()");

? ? CookieRememberMeManager cookieRememberMeManager =new CookieRememberMeManager();

? ? cookieRememberMeManager.setCookie(rememberMeCookie());

? ? //rememberMe cookie加密的密鑰 建議每個(gè)項(xiàng)目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位)

? ? cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));

? ? return cookieRememberMeManager;

}

在securityManager中設(shè)置這個(gè)管理器

securityManager.setRememberMeManager(rememberMeManager());

在控制層上通過

token.setRememberMe(user.isRememberMe());來決定是否開啟這個(gè)功能

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末紊扬,一起剝皮案震驚了整個(gè)濱河市蜒茄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌餐屎,老刑警劉巖檀葛,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腹缩,居然都是意外死亡屿聋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門藏鹊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來润讥,“玉大人,你說我怎么就攤上這事盘寡〕睿” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵竿痰,是天一觀的道長脆粥。 經(jīng)常有香客問我,道長影涉,這世上最難降的妖魔是什么变隔? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮常潮,結(jié)果婚禮上弟胀,老公的妹妹穿的比我還像新娘。我一直安慰自己喊式,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布萧朝。 她就那樣靜靜地躺著岔留,像睡著了一般。 火紅的嫁衣襯著肌膚如雪检柬。 梳的紋絲不亂的頭發(fā)上献联,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音何址,去河邊找鬼里逆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛用爪,可吹牛的內(nèi)容都是我干的原押。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼偎血,長吁一口氣:“原來是場噩夢啊……” “哼诸衔!你這毒婦竟也來了盯漂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤笨农,失蹤者是張志新(化名)和其女友劉穎就缆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谒亦,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竭宰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了份招。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羞延。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脾还,靈堂內(nèi)的尸體忽然破棺而出伴箩,到底是詐尸還是另有隱情,我是刑警寧澤鄙漏,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布嗤谚,位于F島的核電站,受9級(jí)特大地震影響怔蚌,放射性物質(zhì)發(fā)生泄漏巩步。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一桦踊、第九天 我趴在偏房一處隱蔽的房頂上張望椅野。 院中可真熱鬧,春花似錦籍胯、人聲如沸竟闪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炼蛤。三九已至,卻和暖如春蝶涩,著一層夾襖步出監(jiān)牢的瞬間理朋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工绿聘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗽上,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓熄攘,卻偏偏與公主長得像兽愤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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