本文共 10027 字,大约阅读时间需要 33 分钟。
React.js 集成 Spring Boot 开发 Web 应用
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.kt16 directories, 8 files
导入 IDEA 中
#mysqlspring.datasource.url=jdbc:mysql://localhost:3306/reakt?useUnicode=true&characterEncoding=UTF8&useSSL=falsespring.datasource.username=rootspring.datasource.password=rootspring.datasource.driverClassName=com.mysql.jdbc.Driver# Specify the DBMSspring.jpa.database=MYSQL# Show or not log for each sql queryspring.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
package com.reaktboot.reakt.entityimport javax.persistence.*/** * Created by jack on 2017/4/29. */@Entityclass 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: Setoverride fun toString(): String { return "User(id=$id, username='$username', password='$password', roles=$roles)" }}package com.reaktboot.reakt.entityimport javax.persistence.*/** * Created by jack on 2017/4/29. */@Entityclass Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long = -1 @Column(length = 50, unique = true) var role: String = "ROLE_USER"}package com.reaktboot.reakt.daoimport com.reaktboot.reakt.entity.Userimport org.springframework.data.jpa.repository.JpaRepositoryimport org.springframework.data.jpa.repository.Queryimport org.springframework.data.repository.query.Paraminterface UserDao : JpaRepository { @Query(""" select a from #{#entityName} a where a.username = :username """) fun findByUsername(@Param("username") username: String): User?}package com.reaktboot.reakt.daoimport com.reaktboot.reakt.entity.Roleimport org.springframework.data.jpa.repository.JpaRepositoryinterface RoleDao : JpaRepository {}
WebSecurityConfig
package com.reaktboot.reaktimport com.reaktboot.reakt.handler.MyAccessDeniedHandlerimport com.reaktboot.reaktservice.MyUserDetailServiceimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configurationimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilderimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurityimport org.springframework.security.config.annotation.web.builders.HttpSecurityimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurityimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapterimport org.springframework.security.core.userdetails.UserDetailsServiceimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoderimport 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.reaktserviceimport com.reaktboot.reakt.dao.UserDaoimport org.slf4j.LoggerFactoryimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.security.core.authority.SimpleGrantedAuthorityimport org.springframework.security.core.userdetails.UserDetailsimport org.springframework.security.core.userdetails.UserDetailsServiceimport org.springframework.security.core.userdetails.UsernameNotFoundExceptionimport org.springframework.stereotype.Service@Serviceclass 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() roles.forEach { authorities.add(SimpleGrantedAuthority(it.role)) } return org.springframework.security.core.userdetails.User( // 此处为了区分我们本地系统中的 User 实体类,特意列出userdetails 的 User 类的全路径 username, user.password, authorities ) }}
我们使用 nowa:
使用文档:
PC 前端组件库:
前端应用工程目录放到 /Users/jack/KotlinSpringBoot/reakt/src/main/resources 目录下:
前端目录结构如下:
~/KotlinSpringBoot/reakt/src/main/resources/reakt$ lsabc.json mock package-lock.json srchtml 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.js8 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 的样板示例工程:
nowa 使用参考:
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版本,我也体验了一下, 管理项目方便了.不用去文件夹里面翻找了.
Navbar.jsx
import {Component} from 'react';import './Navbar.less';const Menu = require('uxcore-menu')const SubMenu = Menu.SubMenuconst MenuItem = Menu.Itemexport 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 (); }}
参考文档:
转载地址:http://bdrfa.baihongyu.com/