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

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 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/

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
前端开发 JavaScript 关系型数据库
从前端到后端:构建现代化Web应用的技术探索
在当今互联网时代,Web应用的开发已成为了各行各业不可或缺的一部分。从前端到后端,这篇文章将带你深入探索如何构建现代化的Web应用。我们将介绍多种技术,包括前端开发、后端开发以及各种编程语言(如Java、Python、C、PHP、Go)和数据库,帮助你了解如何利用这些技术构建出高效、安全和可扩展的Web应用。
|
20天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
38 0
|
21天前
|
XML 开发框架 Java
Spring轻量级开发框架(二)
Spring轻量级开发框架
44 0
|
24天前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
169 3
|
19天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
42 1
|
19天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
42 0
|
1天前
|
缓存 负载均衡 数据库
优化后端性能:提升Web应用响应速度的关键策略
在当今数字化时代,Web应用的性能对于用户体验至关重要。本文探讨了如何通过优化后端架构和技术手段,提升Web应用的响应速度。从数据库优化、缓存机制到异步处理等多个方面进行了深入分析,并提出了一系列实用的优化策略,以帮助开发者更好地应对日益增长的用户访问量和复杂的业务需求。
8 1
|
1天前
|
缓存 监控 数据库
Flask性能优化:打造高性能Web应用
【4月更文挑战第16天】本文介绍了提升Flask应用性能的七大策略:优化代码逻辑,减少数据库查询,使用WSGI服务器(如Gunicorn、uWSGI),启用缓存(如Flask-Caching),优化数据库操作,采用异步处理与并发(如Celery、Sanic),以及持续监控与调优。通过这些手段,开发者能有效优化Flask应用,适应大型或高并发场景,打造高性能的Web服务。
|
2天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
22 0
【Spring系列】Sping VS Sping Boot区别与联系
|
3天前
|
数据库 开发者 Python
Python中使用Flask构建简单Web应用的例子
【4月更文挑战第15天】Flask是一个轻量级的Python Web框架,它允许开发者快速搭建Web应用,同时保持代码的简洁和清晰。下面,我们将通过一个简单的例子来展示如何在Python中使用Flask创建一个基本的Web应用。