React.js 集成 Spring Boot 开发 Web 应用

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: React.js 集成 Spring Boot 开发 Web 应用1. 创建工程image.pngreakt$ tree ..├── build.

React.js 集成 Spring Boot 开发 Web 应用

1. 创建工程

image.png
reakt$ tree .
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── kotlin
    │   │   └── com
    │   │       └── reaktboot
    │   │           └── reakt
    │   │               └── ReaktApplication.kt
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── kotlin
            └── com
                └── reaktboot
                    └── reakt
                        └── ReaktApplicationTests.kt

16 directories, 8 files

导入 IDEA 中

image.png

2. 配置数据源 application-dev.properties

#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/reakt?useUnicode=true&characterEncoding=UTF8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
# Specify the DBMS
spring.jpa.database=MYSQL
# Show or not log for each sql query
spring.jpa.show-sql=true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto=create-drop
#spring.jpa.hibernate.ddl-auto=update
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

3. 创建User,Role 表

image.png
package com.reaktboot.reakt.entity
import javax.persistence.*

/**
 * Created by jack on 2017/4/29.
 */
@Entity
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = -1
    @Column(length = 50, unique = true)
    var username: String = ""
    var password: String = ""
    @ManyToMany(targetEntity = Role::class, fetch = FetchType.EAGER)
    lateinit var roles: Set<Role>

    override fun toString(): String {
        return "User(id=$id, username='$username', password='$password', roles=$roles)"
    }
}

package com.reaktboot.reakt.entity

import javax.persistence.*


/**
 * Created by jack on 2017/4/29.
 */
@Entity
class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = -1
    @Column(length = 50, unique = true)
    var role: String = "ROLE_USER"
}


package com.reaktboot.reakt.dao

import com.reaktboot.reakt.entity.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

interface UserDao : JpaRepository<User, Long> {

    @Query("""
         select a from #{#entityName} a where a.username = :username
    """)
    fun findByUsername(@Param("username") username: String): User?
}


package com.reaktboot.reakt.dao

import com.reaktboot.reakt.entity.Role
import org.springframework.data.jpa.repository.JpaRepository

interface RoleDao : JpaRepository<Role, Long> {
}



4. 实现登陆权限校验

WebSecurityConfig

package com.reaktboot.reakt

import com.reaktboot.reakt.handler.MyAccessDeniedHandler
import com.reaktboot.reaktservice.MyUserDetailService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.access.AccessDeniedHandler

/**
prePostEnabled :决定Spring Security的前注解是否可用 [@PreAuthorize,@PostAuthorize,..]
secureEnabled : 决定是否Spring Security的保障注解 [@Secured] 是否可用
jsr250Enabled :决定 JSR-250 annotations 注解[@RolesAllowed..] 是否可用.
 */

@Configuration
@EnableWebSecurity
// 开启 Spring Security 方法级安全
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
class WebSecurityConfig : WebSecurityConfigurerAdapter() {

    @Bean
    fun myAccessDeniedHandler(): AccessDeniedHandler {
        return MyAccessDeniedHandler("/403")
    }

    @Bean
    override fun userDetailsService(): UserDetailsService {
        return MyUserDetailService()
    }

    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {
        http.csrf().disable()
        http.authorizeRequests()
            .antMatchers("/", // 首页不拦截
                    "/css/**",
                    "/fonts/**",
                    "/js/**",
                    "/images/**" // 不拦截静态资源
            ).permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            //.loginPage("/login")// url 请求路径,对应 LoginController 里面的 @GetMapping("/login")
            .usernameParameter("username")
            .passwordParameter("password")
            .defaultSuccessUrl("/main").permitAll()
            .and()
            .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler())
//            .exceptionHandling().accessDeniedPage("/403")
            .and()
            .logout().permitAll()

        http.logout().logoutSuccessUrl("/")

    }

    @Throws(Exception::class)
    override fun configure(auth: AuthenticationManagerBuilder) {
        //AuthenticationManager 使用我们的 lightSwordUserDetailService 来获取用户信息
        auth.userDetailsService(userDetailsService())
            .passwordEncoder(passwordEncoder())
    }

    /**
     * 密码加密算法
     *
     * @return
     */
    @Bean
    fun passwordEncoder(): BCryptPasswordEncoder {
        return BCryptPasswordEncoder();
    }
}

MyUserDetailService

package com.reaktboot.reaktservice

import com.reaktboot.reakt.dao.UserDao
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.stereotype.Service

@Service
class MyUserDetailService : UserDetailsService {
    val logger = LoggerFactory.getLogger(MyUserDetailService::class.java)

    @Autowired lateinit var userDao: UserDao


    override fun loadUserByUsername(username: String): UserDetails {
        val user = userDao.findByUsername(username) ?: throw  UsernameNotFoundException(username + " not found")

        logger.info("user = {}", user)
        val roles = user.roles
        val authorities = mutableSetOf<SimpleGrantedAuthority>()
        roles.forEach {
            authorities.add(SimpleGrantedAuthority(it.role))
        }
        return org.springframework.security.core.userdetails.User( // 此处为了区分我们本地系统中的 User 实体类,特意列出userdetails 的 User 类的全路径
                username,
                user.password,
                authorities
        )
    }
}

5. 前端使用 React.js 开发: 目录结构

我们使用 nowa:

https://nowa-webpack.github.io/

使用文档:
https://nowa-webpack.github.io/nowa/

PC 前端组件库:
http://uxco.re/components/button/

image.png

6. 创建 React 前端工程

前端应用工程目录放到 /Users/jack/KotlinSpringBoot/reakt/src/main/resources 目录下:

image.png

前端目录结构如下:

image.png
~/KotlinSpringBoot/reakt/src/main/resources/reakt$ ls
abc.json          mock              package-lock.json src
html              node_modules      package.json      webpack.config.js


~/KotlinSpringBoot/reakt/src/main/resources/reakt$ tree src/
src/
├── app
│   ├── app.js
│   ├── app.less
│   ├── db.js
│   ├── util.js
│   └── variables.js
├── components
│   ├── search-data
│   │   ├── SearchData.jsx
│   │   └── index.js
│   └── search-word
│       ├── SearchWord.jsx
│       └── index.js
├── images
│   └── README.md
└── pages
    ├── demo
    │   ├── PageDemo.jsx
    │   ├── PageDemo.less
    │   ├── index.js
    │   └── logic.js
    └── home
        ├── PageHome.jsx
        ├── PageHome.less
        ├── index.js
        └── logic.js

8 directories, 18 files

前端工程应用单独启动:

jack@jacks-MacBook-Air:~/KotlinSpringBoot/reakt/src/main/resources/reakt$ nowa server

Listening at http://192.168.0.104:3000

浏览器访问: http://192.168.0.104:3000

可以看到 nowa 集成的 uxcore 的样板示例工程:

image.png

nowa 使用参考: https://segmentfault.com/a/1190000009088343

nowa 使用的体验两大精华地方,

不需要学习webpack, 整个前端开发环境都集成了. react入门的小白最喜欢了, 我学webpack头大死了,到现在也没有搞明白. 用了nowa,我就不需要搞明白了.

掌握了nowa的脚手架模板, 整个开发效率提升2倍.
如果说react将组件的复用提高到极限,减少了重复代码的工作量. nowa的自定义脚手架,则把项目文件的复用便捷性提高到极限, 以前要复制一组文件,然后修改文件名/组件名..等等.

现在:

用 nowa init mod 创建一组函数组件
用nowa init rmod 创建一组react组件,
用nowa init page 创建自己个性化的一组文件,
用nwoa init api 创建api资源模块,

创建好了,直接可以写业务代码,不需要复制粘贴啥的了. 当然mod rmod page api 这几个都是按项目和自己习惯,定义过的模板.

gui版本,我也体验了一下, 管理项目方便了.不用去文件夹里面翻找了.

7. 前后端目录集成

image.png
image.png

Navbar.jsx

import {Component} from 'react';
import './Navbar.less';

const Menu = require('uxcore-menu')
const SubMenu = Menu.SubMenu
const MenuItem = Menu.Item


export default class Navbar extends Component {

  static defaultProps = {}

  static propTypes = {}

  constructor(props) {
    super(props);
    this.state = {
      current: '1'
    }
  }


  handleClick(e) {
    console.log('click ', e);
    this.setState({
      current: e.key,
    });
  }


  render() {

    return (
      <div>
        <Menu onClick={this.handleClick.bind(this)} selectedKeys={[this.state.current]} mode="horizontal">
          <Menu.Item key="brand" className = 'brand-style'>
            <h3>Reakt</h3>
          </Menu.Item>
          <Menu.Item key="mail">
            <i className="kuma-icon kuma-icon-email"/>首页
          </Menu.Item>
          <Menu.Item key="app">
            <i className="kuma-icon kuma-icon-wangwang"/>快速开始
          </Menu.Item>
          <SubMenu title={<span><i className="kuma-icon kuma-icon-setting"/>博客文章</span>}>
            <Menu.Item key="setting:1">选项1</Menu.Item>
            <Menu.Item key="setting:2">选项2</Menu.Item>
            <Menu.Item key="setting:3">选项3</Menu.Item>
            <Menu.Item key="setting:4">选项4</Menu.Item>
          </SubMenu>
          <Menu.Item key="alipay">
            <a href="#" target="_blank">关于我们</a>
          </Menu.Item>
        </Menu>

        <Menu
              className="kuma-menu-none-border menu-style"
              defaultOpenKeys={['sub1']}
              selectedKeys={[this.state.current]}
              mode="inline">

          <SubMenu key={"sub1"} title={<span><i className="kuma-icon kuma-icon-email"/><span>Kotlin</span></span>}>
            <MenuItem key={11}>Java</MenuItem>
            <MenuItem key={12}>Scala </MenuItem>
            <MenuItem key={13}>Groovy</MenuItem>
          </SubMenu>

          <SubMenu key={"sub2"}
                   title={<span><i className="kuma-icon kuma-icon-wangwang"/><span>Spring Boot</span></span>}>
            <MenuItem key={21}>Spring MVC</MenuItem>
            <MenuItem key={22}>WebFlux</MenuItem>
            <MenuItem key={23}>Security</MenuItem>
            <MenuItem key={23}>JPA</MenuItem>
          </SubMenu>

          <SubMenu key={"sub3"} title={<span><i className="kuma-icon kuma-icon-wangwang"/><span>React </span></span>}>
            <MenuItem key={31}>Node.js</MenuItem>
            <MenuItem key={32}>Reflux</MenuItem>
            <MenuItem key={33}>ES6</MenuItem>
          </SubMenu>

        </Menu>
      </div>
    );
  }
}

参考文档: https://spring.io/guides/tutorials/react-and-spring-data-rest/

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
18天前
|
JavaScript 前端开发 持续交付
Prettier 高级应用:集成 CI/CD 流水线与插件开发
【10月更文挑战第18天】Prettier 是一款流行的代码格式化工具,它能够自动将代码格式化成一致的风格,从而提高代码的可读性和维护性。对于希望进一步发挥 Prettier 潜力的高级用户而言,将 Prettier 集成到持续集成(CI)和持续部署(CD)流程中,确保每次提交的代码都符合团队标准,是非常重要的。此外,通过开发自定义插件来支持更多语言或扩展 Prettier 的功能也是值得探索的方向。本文将详细介绍这两方面的内容。
37 2
|
25天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
38 4
|
22天前
|
存储 JavaScript 前端开发
掌握现代Web开发的基石:深入理解React与Redux
【10月更文挑战第14天】掌握现代Web开发的基石:深入理解React与Redux
28 0
|
14天前
|
JavaScript 前端开发 持续交付
构建现代Web应用:Vue.js与Node.js的完美结合
【10月更文挑战第22天】随着互联网技术的快速发展,Web应用已经成为了人们日常生活和工作的重要组成部分。前端技术和后端技术的不断创新,为Web应用的构建提供了更多可能。在本篇文章中,我们将探讨Vue.js和Node.js这两大热门技术如何完美结合,构建现代Web应用。
17 4
|
23天前
|
JavaScript Java PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
54 7
|
18天前
|
XML Java 数据格式
提升效率!Spring Boot 开发中的常见失误轻松规避
本文深入探讨了在 Spring Boot 开发中常见的失误,包括不当使用注解、不良异常处理、低效日志记录等,提供了有效的规避策略,帮助开发者提升代码质量和系统性能,构建更健壮、高效的应用程序。
|
3天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
7 0
|
25天前
|
存储 JavaScript 前端开发
深入探索 Vue.js:构建现代 Web 应用的利器
【10月更文挑战第11天】深入探索 Vue.js:构建现代 Web 应用的利器
17 1
|
27天前
|
JavaScript 前端开发 网络架构
如何使用Vue.js构建响应式Web应用
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用
|
27天前
|
JavaScript 前端开发
如何使用Vue.js构建响应式Web应用程序
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用程序
下一篇
无影云桌面