SpringBoot Security 用戶 角色 權(quán)限 控制

在沒(méi)有用權(quán)限框架之前田弥,我們做權(quán)限控制的時(shí)候一般思路是這樣的:
1.登錄的時(shí)候從數(shù)據(jù)庫(kù)中檢索user所擁有的privileges存在內(nèi)存中
2.在用戶發(fā)出請(qǐng)求的時(shí)候陪捷,將請(qǐng)求信息與用戶所擁有的權(quán)限對(duì)比能庆。

spring security也有這種實(shí)現(xiàn)渠啊,然而我們要做的就是:
自定義過(guò)濾器,代替原有的FilterSecurityInterceptor過(guò)濾器锁孟,實(shí)現(xiàn)
UserDetailsService (儲(chǔ)存用戶所有角色)
InvocationSecurityMetadataSourceService(訪問(wèn)資源所需要的角色集合)
AccessDecisionManager(判斷用戶請(qǐng)求的資源 是否能通過(guò))


思路很明了看看實(shí)現(xiàn):
說(shuō)明:
用了jpa 當(dāng)時(shí)參考了一些資料,走了很多彎路發(fā)現(xiàn)沒(méi)有很大作用,
還是習(xí)慣自己寫sql允耿,所以這里只是用來(lái)創(chuàng)建表格,反正以后擴(kuò)展crud會(huì)用到扒怖。
真正做持久化的是mybatis较锡,這兩個(gè)整合在一起了,只是多創(chuàng)建了一個(gè)接口而已盗痒。

準(zhǔn)備工作

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

        <!-- thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!-- jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
# server
server:
  port: 8888
spring:
# dataSource
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/nul_blog?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: admin
    password: root
# jpa
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database: mysql
# thymeleaf
  thymeleaf:
    prefix: classpath:/templates/
    cache: false
    suffix: .html
# mybatis
mybatis:
  mapper-locations: mappers/*.xml

一.用戶 角色 權(quán)限 三張表
很普通的bean蚂蕴,user類并沒(méi)有繼承userdetails

@Entity
@Table(name = "sys_user")
public class SysUser implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
    @NotNull
    @Column(unique = true)
    private String username;
    @NotNull
    private String password;
    @ManyToMany
    @JoinTable(name = "sys_user_role",joinColumns = {@JoinColumn(name = "sys_user_id")},inverseJoinColumns={@JoinColumn(name = "sys_role_id")})
    private List<SysRole> roles;
    //... getter and settter
}
@Entity
@Table(name = "sys_role")
public class SysRole implements Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
    @NotNull
    private String name;
    @ManyToMany
    @JoinTable(name = "sys_role_permission",
joinColumns = {@JoinColumn(name = "sys_role_id")},
inverseJoinColumns={@JoinColumn(name = "sys_permission_id")})
    private List<SysPermission> permissions;
     //... getter and settter
}
@Entity
@Table(name = "sys_permission")
public class SysPermission implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
    @NotNull
    private Long pid;
    @NotNull
    private String name;
    @NotNull
    private String url;
    @NotNull
    private String description;
    //... getter and settter
}
public class SysRolePermisson {
    //角色
    private String roleId;
    private String roleName;

    //權(quán)限
    private String permissionId;
    private String url;
    //... getter and settter
}
image.png

關(guān)于數(shù)據(jù)的設(shè)計(jì)我是這樣想的:

基礎(chǔ)用戶和高級(jí)用戶區(qū)別是擁有多的權(quán)限
所以多的權(quán)限就把他賦給另個(gè)角色

所以一個(gè)用戶最少擁有基礎(chǔ)用戶的權(quán)限
其次才擁有高級(jí)用戶的權(quán)限

@Mapper
public interface SysUserMapper {

    /**
     * 通過(guò)username查找 user
     * username是唯一的前提
     *
     * @param username
     * @return SysUser
     */
    SysUser findUserByUsername(String username);

    /**
     * 通過(guò)用戶名 查找·
     * @param username
     * @return List<SysRole>
     */
    List<SysRole> findRolesByUsername(String username);

    /**
     * 通過(guò)用戶名 查找權(quán)限
     * @param username
     * @return List<SysPermission>
     */
    List<SysPermission> findPermissionsByUsername(String username);

    List<SysRolePermisson> findAllRolePermissoin();

}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sandnul.repository.SysUserMapper">
    <select id="findUserByUsername" resultType="com.sandnul.domain.SysUser">
        select id,password,username from SYS_user WHERE username = #{username}
    </select>

    <select id="findPermissionsByUsername" resultType="com.sandnul.domain.SysPermission">
        select sp.*
        from sys_user su
        left join sys_user_role  sur on su.id = sur.sys_user_id
        left join sys_role_permission srp on sur.sys_role_id = srp.sys_role_id
        left join sys_permission sp on srp.sys_permission_id = sp.id
        where su.username = #{username}
    </select>
    
    <select id="findRolesByUsername" resultType="com.sandnul.domain.SysRole">
        select sr.*
        from sys_user su
        left join sys_user_role  sur on su.id = sur.sys_user_id
        left join sys_role sr on sur.sys_role_id = sr.id
        where su.username = #{username}
    </select>
    
    <select id="findAllRolePermissoin" resultType="com.sandnul.domain.SysRolePermisson">
        select sr.id roleId ,sr.name roleName,sp.id permissionId,sp.url
        from sys_role_permission srp
        left join sys_role sr  on sr.id = srp.sys_role_id
        left join sys_permission sp on srp.sys_permission_id = sp.id
    </select>
</mapper>
public interface SysUserRepository extends JpaRepository<SysUser, Long>{

}

以下就是思路的實(shí)現(xiàn)

二、替換原先的攔截器
說(shuō)實(shí)話我不知道這個(gè)攔截器和原先的攔截器有什么不同俯邓,我去掉后發(fā)現(xiàn)權(quán)限亂了
骡楼,估計(jì)還要讀一些源碼吧。

@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{

    @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {

        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            //執(zhí)行下一個(gè)攔截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    @Override
    public void destroy() {

    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }
}


三稽鞭、重寫UserDetailsService,登錄認(rèn)證
認(rèn)證是由 AuthenticationManager 來(lái)管理的鸟整,但是真正進(jìn)行認(rèn)證的是 AuthenticationManager 中定義的 AuthenticationProvider。Spring Security 默認(rèn)會(huì)使用DaoAuthenticationProvider朦蕴。DaoAuthenticationProvider 在進(jìn)行認(rèn)證的時(shí)候需要一個(gè) UserDetailsService 來(lái)獲取用戶的信息 UserDetails篮条。改變認(rèn)證的方式,就實(shí)現(xiàn) UserDetailsService吩抓,返回我們自己userdetails涉茧。

@Component
public class MyCustomUserService implements UserDetailsService{

    @Autowired
    private SysUserMapper sysUserMapper;

    /**
     * 將用的所有角色存儲(chǔ)于用戶信息中
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = sysUserMapper.findUserByUsername(username);
        if(user == null)
            throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));

        //獲取所有請(qǐng)求的url
        //List<SysPermission> sysPermissions = sysUserMapper.findPermissionsByUsername(user.getUsername());
        List<SysRole> sysRoles = sysUserMapper.findRolesByUsername(user.getUsername());

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        for (SysRole sysRole : sysRoles) {
            //封裝用戶信息和角色信息 到 SecurityContextHolder全局緩存中
            grantedAuthorities.add(new SimpleGrantedAuthority(sysRole.getName()));
        }
        return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
    }
}

四、實(shí)現(xiàn)FilterInvocationSecurityMetadataSource
作用為了將所有資源和資源對(duì)應(yīng)需要的角色存在map中
用戶請(qǐng)求資源的時(shí)候能夠返回資源所對(duì)應(yīng)的角色集合給
決策器(MyAccessDecisionManager)

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class);

    /**
     * 通過(guò) 實(shí)現(xiàn)UserDetailService 來(lái)進(jìn)行驗(yàn)證
     */
    @Autowired
    private MyCustomUserService myCustomUserService;

    /**
     *
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{

        //校驗(yàn)用戶
        auth.userDetailsService(myCustomUserService)
                //校驗(yàn)密碼
                .passwordEncoder(new PasswordEncoder() {

            @Override
            public String encode(CharSequence rawPassword) {
                return Md5Util.MD5(String.valueOf(rawPassword));
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return encodedPassword.equals(Md5Util.MD5(String.valueOf(rawPassword)));
            }
        });

    }


    /**
     * 創(chuàng)建自定義的表單
     *
     * 頁(yè)面琴拧、登錄請(qǐng)求降瞳、跳轉(zhuǎn)頁(yè)面等
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","index","/login","/css/**","/js/**")//允許訪問(wèn)
                .permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")//攔截后get請(qǐng)求跳轉(zhuǎn)的頁(yè)面
                .defaultSuccessUrl("/hello")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }
}
@Component
public class MyInvocationSecurityMetadataSourceService  implements
        FilterInvocationSecurityMetadataSource {

    @Autowired
    private SysUserMapper sysUserMapper;

    /**
     * 每一個(gè)資源所需要的角色 Collection<ConfigAttribute>決策器會(huì)用到 不急弄清楚
     */
    private static HashMap<String, Collection<ConfigAttribute>> map =null;
    
    //初始化 所有資源 對(duì)應(yīng)的角色
    public void loadResourceDefine(){
        map = new HashMap<>();
        //權(quán)限資源 和 角色對(duì)應(yīng)的表  也就是 角色 權(quán)限中間表
        List<SysRolePermisson> rolePermissons = sysUserMapper.findAllRolePermissoin();

        //每個(gè)資源 所需要的角色
        for (SysRolePermisson rolePermisson : rolePermissons) {

            String url = rolePermisson.getUrl();
            String roleName = rolePermisson.getRoleName();
            ConfigAttribute role = new SecurityConfig(roleName);

            if(map.containsKey(url)){
                map.get(url).add(role);
            }else{
                map.put(url,new ArrayList<ConfigAttribute>(){{
                    add(role);
                }});
            }
        }
    }

    /**
     * 我的理解是 方法會(huì)被調(diào)用,然后返回資源所需要的角色
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if(map ==null)
            loadResourceDefine();
        //object 中包含用戶請(qǐng)求的request 信息
        HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            String url = iter.next();
            if(new AntPathRequestMatcher(url).matches(request)) {
                return map.get(url);
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

四蚓胸、決策器
主要的方法返回值是void挣饥,可以想到既然判斷是否有權(quán)通過(guò),
那沒(méi)通過(guò)肯定就是拋出異常沛膳,讓外層捕捉扔枫。

@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

    private final static Logger logger = LoggerFactory.getLogger(MyAccessDecisionManager.class);

    /**
     * 判定 是否含有權(quán)限
     * @param authentication  CustomUserService.loadUserByUsername() 封裝的用戶信息
     * @param object    request請(qǐng)求信息
     * @param configAttributes InvocationSecurityMetadataSourceService.getAttributes()  中每個(gè)資源可訪問(wèn)的角色集合
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

        if(null== configAttributes || configAttributes.size() <=0) {
            return;
        }
        String needRole;
        for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
            needRole = iter.next().getAttribute();


            for(GrantedAuthority ga : authentication.getAuthorities()) {
                if(needRole.trim().equals(ga.getAuthority().trim())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("no privilege");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

請(qǐng)求和頁(yè)面

@Controller
public class TestController {

    @GetMapping(value = {"/","index"})
    public String index(){

        return "index";
    }

    @GetMapping(value = "hello")
    public String hello(){

        return "hello";
    }

    @GetMapping(value = "login")
    public String login(){

        return "login";
    }


    @GetMapping(value = "admin")
    public String admin(Model model){

        model.addAttribute("title","標(biāo)題");
        model.addAttribute("content","內(nèi)容");
        model.addAttribute("extraInfo","你是admin");
        return "admin";
    }
}
admin.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <meta charset="UTF-8"/>
    <title sec:authentication="name"></title>
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
    <style type="text/css">
        body {
            padding-top: 50px;
        }

        .starter-template {
            padding: 40px 15px;
            text-align: center;
        }
    </style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a th:href="@{/}">首頁(yè)</a></li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">
    <div class="starter-template">
        <h1 th:text="${title}"></h1>
        <p class="bg-primary" th:text="${content}"></p>
        <div sec:authorize="hasRole('ROLE_ADMIN')">
            <p class="bg-info" th:text="${extraInfo}"></p>
        </div>
        <div sec:authorize="hasRole('ROLE_USER')">
            <p class="bg-info">無(wú)更多顯示信息</p>
        </div>
        <form th:action="@{/logout}" method="post">
            <input type="submit" class="btn btn-primary" value="注銷"/>
        </form>
    </div>
</div>
</body>
</html>
hello.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <meta charset="UTF-8" />
    <title>Title</title>
</head>
<body>
<h1>hello spring boot with security</h1>
<form th:action="@{/logout}" method="post">
    <input type="submit" class="btn btn-primary" value="注銷"/>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Title</title>
</head>
<body>
index
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>登錄</title>
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
    <style type="text/css">
        body {
            padding-top: 50px;
        }

        .starter-template {
            padding: 40px 15px;
            text-align: center;
        }
    </style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a th:href="@{/}">首頁(yè)</a></li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">
    <div class="starter-template">
        <p th:if="${param.logout}" class="bg-warning">已注銷</p>
        <p th:if="${param.error}" class="bg-danger" th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}">有錯(cuò)誤</p>
        <h2>使用賬號(hào)密碼登錄</h2>
        <form name="form" th:action="@{/login}" action="/login" method="post">
            <div class="form-group">
                <label for="username">賬號(hào)</label>
                <input type="text" class="form-control" name="username" value="" placeholder="賬號(hào)"/>
            </div>
            <div class="form-group">
                <label for="password">密碼</label>
                <input type="password" class="form-control" name="password" placeholder="密碼"/>
            </div>
            <input type="submit" id="login" value="Login" class="btn btn-primary"/>
        </form>
    </div>
</div>
</body>
</html>

ls的賬號(hào)去登錄成功會(huì)跳轉(zhuǎn)到hello頁(yè)面然而,請(qǐng)求admin頁(yè)面 則會(huì)報(bào)錯(cuò)no privilege


以下是參考的一些文章
http://blog.csdn.net/u012373815/article/details/54633046
http://blog.csdn.net/code__code/article/details/53885510
http://blog.csdn.net/u012367513/article/details/38866465
https://www.w3cschool.cn/springsecurity/uzq31ii7.html


源碼
https://github.com/sandnul025/springBoot-springSecurity.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锹安,一起剝皮案震驚了整個(gè)濱河市短荐,隨后出現(xiàn)的幾起案子倚舀,更是在濱河造成了極大的恐慌,老刑警劉巖忍宋,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痕貌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡糠排,警方通過(guò)查閱死者的電腦和手機(jī)舵稠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)入宦,“玉大人哺徊,你說(shuō)我怎么就攤上這事∏颍” “怎么了落追?”我有些...
    開(kāi)封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)涯肩。 經(jīng)常有香客問(wèn)我轿钠,道長(zhǎng),這世上最難降的妖魔是什么宽菜? 我笑而不...
    開(kāi)封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任谣膳,我火速辦了婚禮竿报,結(jié)果婚禮上铅乡,老公的妹妹穿的比我還像新娘。我一直安慰自己烈菌,他們只是感情好阵幸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著芽世,像睡著了一般挚赊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上济瓢,一...
    開(kāi)封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天荠割,我揣著相機(jī)與錄音,去河邊找鬼旺矾。 笑死蔑鹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的箕宙。 我是一名探鬼主播嚎朽,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柬帕!你這毒婦竟也來(lái)了哟忍?” 一聲冷哼從身側(cè)響起狡门,我...
    開(kāi)封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锅很,沒(méi)想到半個(gè)月后其馏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爆安,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年尝偎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹏控。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡致扯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出当辐,到底是詐尸還是另有隱情抖僵,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布缘揪,位于F島的核電站耍群,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏找筝。R本人自食惡果不足惜蹈垢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望袖裕。 院中可真熱鬧曹抬,春花似錦、人聲如沸急鳄。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疾宏。三九已至张足,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坎藐,已是汗流浹背为牍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岩馍,地道東北人碉咆。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像兼雄,于是被迫代替她去往敵國(guó)和親吟逝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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