Spring Security -- 安全

概念
認(rèn)證:即登錄瘾带,authentication
授權(quán):即允許某種操作鼠哥,authorization
會話:即保持已登錄狀態(tài)
RBAC:Role-Based Access Control ,基于角色的訪問控制
業(yè)務(wù)系統(tǒng):即前臺用戶系統(tǒng)
內(nèi)管系統(tǒng):即后臺管理系統(tǒng)

Spring Boot Security 應(yīng)用組成

1看政、初始化Spring Boot 應(yīng)用
2朴恳、在pom中增加依賴管理

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.spring.platform</groupId>
                <artifactId>platform-bom</artifactId> <!-- Spring Framework依賴管理,來自Spring IO Platform項目 -->
                <version>Cairo-SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId> <!-- Spring Cloud 依賴管理 -->
                <version>Greenwich.M3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

加了依賴管理后允蚣,設(shè)置依賴不需要指明版本于颖,且不需要以下配置:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
    </parent>

3、配置Maven依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
        </dependency>

4厉萝、從數(shù)據(jù)庫中查詢用戶詳情的實現(xiàn)類

@Component
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 根據(jù)用戶名恍飘,從數(shù)據(jù)庫找出用戶信息
        // 參數(shù)依次為:用戶名榨崩,數(shù)據(jù)庫里記錄的密碼,可用章母,未過期母蛛,密碼未過期乳怎,未被鎖定,權(quán)限列表
        // Spring Security 會 自動調(diào)用 PasswordEncoder.match() 來判斷密碼是否正確
        return new org.springframework.security.core.userdetails.User(username, passwordEncoder.encode("密碼"), true, true,
                true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_角色1,權(quán)限1"));
    }
}

5秫逝、登錄成功 违帆、 登錄失敗刷后、登出成功尝胆、賬號被踢出含衔、無訪問權(quán)限 處理類

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        // 返回一個 json
        response.getWriter().write(objectMapper.writeValueAsString(authentication)); // authentication 里有權(quán)限列表
    }
}
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        // 返回一個 json
        response.getWriter().write(objectMapper.writeValueAsString(e)); // e 認(rèn)證失敗的原因
    }
}
@Component
public class LogOutHandler implements LogoutSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        // 返回一個 json
        response.getWriter().write(objectMapper.writeValueAsString(authentication));
    }
}
@Component
public class ExpiredSessionStrategy  implements SessionInformationExpiredStrategy {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        event.getResponse().setContentType("application/json;charset=UTF-8");
        RestfulResult result = new RestfulResult(-1, "您的賬號已在別處登錄");
        event.getResponse().getWriter().write(objectMapper.writeValueAsString(result));
    }
}
@Component
public class AuthenticationEntryPoint implements org.springframework.security.web.AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
        response.setStatus(HttpStatus.FOUND.value());
        response.setHeader("location", "https://**.com/web/sign_out");
    }
}

6、Spring Security 配置類

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private LogOutHandler logOutHandler;
    @Autowired
    private ExpiredSessionStrategy expiredSessionStrategy;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()      // 使用表單登錄
                .loginPage("/login.html")  // 未登錄時重定向到登錄頁面抑进, 不指定則使用Spring security 默認(rèn)提供的登錄頁面
                .loginProcessingUrl("/api/login")  // 指定 登錄接口 url
                .successHandler(loginSuccessHandler)  // 指定登錄成功處理類寺渗,不指定則重定向
                .failureHandler(loginFailureHandler) // 指定登錄失敗處理類信殊,不指定則重定向
                .and()
                .authorizeRequests()   // 開始授權(quán)配置
                .antMatchers("/*.html").permitAll()  // 對*.html 的請求涡拘,無需權(quán)限
                .antMatchers(HttpMethod.POST, "/manage/*").hasRole("manager") // 對 /manage/* 的請求据德,需要擁有manager角色
                .antMatchers("/client/*").hasAuthority("client") // 對 /client/* 的請求,需要擁有client權(quán)限
                .anyRequest().authenticated()           // 針對所有請求橱野,進行身份認(rèn)證
                     
                .and()
                .logout()   // 開始 登出配置
                .logoutUrl("/signOut")  // 登出接口水援,默認(rèn)為 /logout
                .logoutSuccessUrl("/login.html")  // 登出重定向到的路徑蜗元,默認(rèn)為loginPage
                .logoutSuccessHandler(logOutHandler) // 與logoutSuccessUrl互斥
                .deleteCookies("JSESSION") // 登出時 清理 cookie
                .and()
                .csrf()   // 開始csrf配置
                .disable() // 放開csrf防御
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);    // 無權(quán)限訪問的處理
                // 只配置 loginPage 時奕扣,可能出現(xiàn) “無訪問權(quán)限時 瀏覽器彈出一個默認(rèn)登錄框”惯豆,為規(guī)避這種情況循帐,可以配置authenticationEntryPoint()

                http.sessionManagement().maximumSessions(1)  // 同一賬號只允許一處登錄
                .maxSessionsPreventsLogin(false)    // 允許后登錄者踢出先登錄者
                .expiredSessionStrategy(expiredSessionStrategy);  // 被踢出時的請求響應(yīng)
        super.configure(http);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 這是Spring提供的一個密碼加密器拄养,加鹽散列瘪匿,并將鹽拼入散列值棋弥,可用防止散列撞庫
        return new BCryptPasswordEncoder(); // 也可以自己實現(xiàn)一個 PasswordEncoder
    }

    @Bean
    // 允許跨域配置
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin(CorsConfiguration.ALL);  // 或 config.setAllowedOriginPatterns(Collections.singletonList("*"));
        config.addAllowedHeader("*");
        config.addAllowedMethod("OPTIONS");  // AllowedMethod 必須羅列顽染,而不能用通配符 *
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

圖片驗證碼驗證

1粉寞、配置Maven依賴

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
        </dependency>

2唧垦、認(rèn)證異常類

// AuthenticationException 是抽象類液样,不能實例化,因此需要自定義一個 驗證碼異常類
public class ValidateCodeException extends AuthenticationException {
    public ValidateCodeException(String msg) {
        super(msg);
    }
}

3麸祷、驗證碼過濾器

@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 只過濾登錄接口
        if (StringUtils.equals("/api/login", request.getRequestURI()) && StringUtils.equalsIgnoreCase("post", request.getMethod())) {
            try {
                validate(new ServletWebRequest(request));
            }
            catch (ValidateCodeException e) {
                loginFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
    private void validate(ServletWebRequest request) throws ServletRequestBindingException {
        // 這里從session中取出驗證碼值進行比對
        throw new ValidateCodeException("驗證碼不匹配");
    }
}

4摇锋、Spring Security配置類

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private ValidateCodeFilter validateCodeFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 在驗證賬號 之前 驗證 圖片驗證碼
        http.addFilterBefore( validateCodeFilter, UsernamePasswordAuthenticationFilter.class )
                .formLogin();
        super.configure(http);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 這是Spring提供的一個密碼加密器荸恕,加鹽散列融求,并將鹽拼入散列值生宛,可用防止散列撞庫
        return new BCryptPasswordEncoder(); // 也可以自己實現(xiàn)一個 PasswordEncoder
    }
}

短信登錄

1肮柜、仿照 UsernamePasswordAuthenticationToken审洞,定義Token類

public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    private final Object principal;

    public SmsCodeAuthenticationToken(String mobile) {
        super(null);
        this.principal = mobile;
        setAuthenticated(false);
    }

    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }


    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }
        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

2仰剿、仿照 UsernamePasswordAuthenticationFilter痴晦,定義Filter類

public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private String mobileParameter = "mobile";    // 字段名
    private boolean postOnly = true;

    public SmsCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/api/mobileLogin", "POST"));  // 短信登錄接口
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String mobile = obtainMobile(request);
        if (mobile == null) {
            mobile = "";
        }
        mobile = mobile.trim();
        SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }


    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }


    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public void setMobileParameter(String parameter) {
        this.mobileParameter = parameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getMobileParameter() {
        return mobileParameter;
    }
}

3部凑、仿照 DaoAuthenticationProvider 實現(xiàn) Provider類

public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        
        UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal());

        if (user == null) {
            throw new InternalAuthenticationServiceException("無法獲取用戶信息");
        }
        
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
        
        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}

4、短信驗證碼過濾器

// 與圖片驗證碼過濾器 類似

5辉词、配置 Filter類 和 Provider類

@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    
    @Autowired
    private AuthenticationFailureHandler imoocAuthenticationFailureHandler;
    
    @Autowired
    private MyUserDetailsService userDetailsService;
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        
        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
        
        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
        
        http.authenticationProvider(smsCodeAuthenticationProvider)
            .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

6敷搪、Spring Security 配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private ValidateCodeFilter validateCodeFilter;
    @Autowired
    private SmsCodeAuthenticationSecurityConfig authenticationSecurityConfig;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore( validateCodeFilter, UsernamePasswordAuthenticationFilter.class )
                .formLogin() 
                .apply(authenticationSecurityConfig);
        super.configure(http);
    }
}

禁用 Spring Security

注解啟動類 ServletInitializer

@EnableAutoConfiguration(exclude = {
        SecurityAutoConfiguration.class
})

RBAC數(shù)據(jù)模型

表:用戶表嫂便,角色表闸与,資源(權(quán)限)表,用戶角色關(guān)系表厂画,角色資源關(guān)系表
資源表:存儲權(quán)限控制目標(biāo)袱院,例如:菜單忽洛、按鈕环肘、URL

最佳實踐
1、業(yè)務(wù)系統(tǒng)苍在,一般權(quán)限控制比較簡單,無需RBAC
2、內(nèi)管系統(tǒng)续誉,需要RBAC酷鸦,并且系統(tǒng)中有管理RBAC數(shù)據(jù)的界面
3、資源表的值可以設(shè)置為 "對象.操作"嘹裂,例如 "order.delete"表示訂單的刪除權(quán)限摔握,"order.delete"表示訂單的刪除權(quán)限,"coupon.all"表示優(yōu)惠券的所有權(quán)限
4伊磺、前端 根據(jù) RBAC 數(shù)據(jù) 隱藏 入口(菜單删咱,按鈕)
5、后端 根據(jù) RBAC 數(shù)據(jù)表存儲的 角色 與 可訪問的 URL 控制訪問

Spring Security 整合 RBAC
1摘能、RBAC 權(quán)限判斷 類

// 案例 1
@Component("rbacService")
public class RbacService {
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if(principal instanceof UserDetails){
            // 用戶名
            String username = ((UserDetails)principal).getUsername();
            // 根據(jù)用戶名 找到 當(dāng)前用戶可訪問的 url 列表
            Set<String> urls = new HashSet<String>();
            for(String url : urls){
                if(antPathMatcher.match(url, request.getRequestURI())){
                    return true;
                }
            }
        }
        return false;
    }
}
// 案例 2
// 權(quán)限的格式為 module.method团搞,判斷當(dāng)前url是否包含在權(quán)限列表里
@Component("rbacService")
public class RbacService {
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        // 未登錄
        if(authentication.getPrincipal() instanceof String){
            return false;
        }
        Collection<? extends GrantedAuthority> authorityList = authentication.getAuthorities();
        String method = request.getMethod().toLowerCase();
        String path = request.getServletPath();
        for (GrantedAuthority authority : authorityList) {
            // authority 的格式為 module.allow
            // allow取值: get莺丑、post梢莽、put昏名、delete阵面、all,默認(rèn)為all
            String[] authoritySegment = authority.getAuthority().split("\\.");
            String module = authoritySegment[0];
            String allow = "all";
            if(authoritySegment.length > 1){
                allow = authoritySegment[1];
            }
            String regexStart = "/admin/" + module + "/";
            String regexEnd = "/admin/" + module;
            if (path.startsWith(regexStart) || path.endsWith(regexEnd) || module.equals("all")) {
                // 權(quán)限列表里有的模塊仑扑,才允許訪問镇饮,且請求方法需要匹配
                if (method.equals("get") || method.equals(allow) || allow.equals("all")) {
                    return true;
                }
            }
        }
        return false;
    }
}

2储藐、Spring Security 配置

        http.authorizeRequests()
                .antMatchers("/*.html").permitAll()  // 對*.html 的請求嘶是,放開所有權(quán)限
                 // 進行權(quán)限判斷聂喇,此外仍然會判斷是否認(rèn)真
                .antMatchers("/manage").access("@rbacService.hasPermission(request, authentication)")   // 放在authenticated()前面
                .anyRequest().authenticated()      // anyRequest()必須放在authorizeRequests的最后,且只能有一個
        super.configure(http);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浮梢,一起剝皮案震驚了整個濱河市秕硝,隨后出現(xiàn)的幾起案子远豺,更是在濱河造成了極大的恐慌坞嘀,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棺滞,死亡現(xiàn)場離奇詭異继准,居然都是意外死亡矮男,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門崔泵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猪瞬,“玉大人,你說我怎么就攤上這事含思「饰睿” “怎么了线婚?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵塞弊,是天一觀的道長游沿。 經(jīng)常有香客問我诀黍,道長,這世上最難降的妖魔是什么枣宫? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任也颤,我火速辦了婚禮翅娶,結(jié)果婚禮上好唯,老公的妹妹穿的比我還像新娘渠啊。我一直安慰自己替蛉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镣煮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镊折。 梳的紋絲不亂的頭發(fā)上恨胚,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天赃泡,我揣著相機與錄音,去河邊找鬼升熊。 笑死级野,一個胖子當(dāng)著我的面吹牛勺阐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蟆豫,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼十减,長吁一口氣:“原來是場噩夢啊……” “哼帮辟!你這毒婦竟也來了由驹?” 一聲冷哼從身側(cè)響起昔园,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甥郑,失蹤者是張志新(化名)和其女友劉穎荤西,沒想到半個月后邪锌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體觅丰,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡舶胀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嚣伐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糖赔。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖轩端,靈堂內(nèi)的尸體忽然破棺而出放典,到底是詐尸還是另有隱情,我是刑警寧澤基茵,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布奋构,位于F島的核電站,受9級特大地震影響拱层,放射性物質(zhì)發(fā)生泄漏弥臼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一根灯、第九天 我趴在偏房一處隱蔽的房頂上張望纳猪。 院中可真熱鬧,春花似錦、人聲如沸脚祟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娃循。三九已至笛质,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肩杈。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工编丘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留索守,地道東北人敞斋。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親暑椰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

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