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

react

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

前端工程应用单独启动:

[email protected]:~/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/

以上是 React.js 集成 Spring Boot 开发 Web 应用 的全部内容, 来源链接: utcz.com/z/384067.html

回到顶部