我用SpringBoot(Kotlin)和Freemarker制作了一个登录示例

因为工作需要,我开始使用SpringBoot和FreeMarker,尝试实现一个简单的登录页面。
另外,由于似乎要在前端使用Vue,所以顺便尝试将Webpack和Gradle整合起来,实现加载Vue。

由于SpringBoot的Kotlin仍然缺乏日语文档,因此我将在说明过程中附上源码,并简要概括要点。

源代码
https://github.com/renoinn/springboot-freemarker-vue-sample

只要在Docker或类似平台上准备好数据库,应该就可以直接运行。

构建.gradle

buildscript {
    ext {
        kotlinVersion = '1.2.51'
        springBootVersion = '2.0.5.RELEASE'
    }
    repositories {
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
        classpath "com.moowork.gradle:gradle-node-plugin:1.2.0"
    }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'kotlin-allopen'
apply plugin: 'kotlin-jpa'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.moowork.node'

group = 'com.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // spring
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('org.springframework.boot:spring-boot-starter-freemarker')
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.session:spring-session-core')
    runtime('org.springframework.boot:spring-boot-devtools')
    compileOnly "org.springframework.boot:spring-boot-configuration-processor"

    // etc
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    compile('com.fasterxml.jackson.module:jackson-module-kotlin')
    compile('no.api.freemarker:freemarker-java8:1.2.0')

    // mysql
    runtime('mysql:mysql-connector-java')

    // test
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
}

node {
    version = '9.11.2'
    npmVersion = '6.4.1'
    download = true
}

// Webpackでのビルドを実行するタスク
task webpack(type: NodeTask, dependsOn: 'npmInstall') {
    def osName = System.getProperty("os.name").toLowerCase()
    if (osName.contains("windows")) {
        script = project.file('node_modules/webpack/bin/webpack.js')
    } else {
        script = project.file('node_modules/.bin/webpack')
    }
}

// processResourcesタスクの前に上述のwebpackタスクを実行する
processResources.dependsOn 'webpack'

clean.delete << file('node_modules')

首先是build.gradle。在使用Kotlin进行Spring Boot开发时,kotlin-allopen、kotlin-noarg和kotlin-jpa可以帮助简化一些稍微繁琐的部分。
另外,为了与webpack进行协同,也需要添加gradle-node-plugin。

WebSecurityConfig.kt 的中文释义。

@Configuration
@EnableWebSecurity
class WebSecurityConfig : WebSecurityConfigurerAdapter() {

    @Override
    @Throws(Exception::class)
    override fun configure(web: WebSecurity) {
        // これらにマッチするURLへのアクセスは無視する
        web.ignoring().antMatchers(
                "/images/**",
                "/css/**",
                "/js/**",
                "/webjars/**")
    }

    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {
        http.csrf().disable()
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin() // フォームでのログインをする宣言
                .loginProcessingUrl("/login") // ログイン処理をするエンドポイント。formのactionでここを指定する
                .loginPage("/") // ログインフォームのURL
                .defaultSuccessUrl("/home") // ログイン成功後のURL

        http.logout()
                .logoutRequestMatcher(AntPathRequestMatcher("/logout**")) // マッチするリクエストがきたらログアウトする
                .logoutSuccessUrl("/") // ログアウト後のURL
    }

    @Configuration
    class AuthenticationConfiguration : GlobalAuthenticationConfigurerAdapter() {
        @Autowired
        var userDetailsService: UserDetailsServiceImpl = UserDetailsServiceImpl()

        @Throws(Exception::class)
        override fun init(auth: AuthenticationManagerBuilder) {
            auth.userDetailsService<UserDetailsService>(userDetailsService)
                    .passwordEncoder(BCryptPasswordEncoder())

        }
    }
}

接下来是针对Spring Security的配置。通过覆盖WebSecurityConfigurerAdapter内的configure方法来添加设置。
还有一部分,覆盖了GlobalAuthenticationConfigurerAdapter内的init方法。需要将PasswordEncoder传递给AuthenticationManagerBuilder才能启用Spring Security,所以要注意。即使是以明文保存密码的情况,也必须使用NoOpPasswordEncoder进行传递。我认为几乎不会使用明文保存密码这样的情况。

在使用Spring Security时,需要准备一个继承UserDetails的实体类和实现UserDetailsService接口的实现类。

User.kt = 用户.kt

UserDetailsServiceImpl.kt文件

@Service
class UserDetailsServiceImpl : UserDetailsService {
    @Autowired
    lateinit var userRepository: UserRepository

    override fun loadUserByUsername(username: String): UserDetails {
        val user = findByUsername(username)
        if (!user.isPresent)
            throw UsernameNotFoundException("Username $username is not found")
        if (!user.get().isAccountNonLocked)
            throw UsernameNotFoundException("Username $username is locked")
        return user.get().copy(authorities = getAuthorities())
    }

    private fun getAuthorities() = AuthorityUtils.createAuthorityList("ROLE_USER")

    fun findByUsername(username: String): Optional<User> {
        val user = userRepository.findByUsername(username)
        return user
    }
}

只需一个选项,将以下内容用中文进行重述:

只要查看UserDetails实体后,UserDetailsServiceImpl必须重写loadUserByUsername来返回UserDetails。在这种情况下,返回的UserDetails对象的authorities属性值必须以ROLE_前缀开始,比如ROLE_USER或ROLE_ADMIN,注意这一点。

webpack配置文件。


const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  entry: './src/main/js/app.js',
  output: {
    filename: 'js/bundle.js',
    path: __dirname + '/build/resources/main/static/'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader'
            })
          }
        }
      },
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  plugins: [
    new ExtractTextPlugin("css/styles.css"),
  ]
}
;

webpack应该没有做任何特别复杂的事情。将文件放置在/build/resources/main/static/js目录下,就可以通过https://hoge.com/js进行访问,因此output设置为那里。

因为我还在学习阶段,所以非常欢迎像这样的建议性评论和Pull请求。

请提供参考的URL链接。

    • https://qiita.com/nvtomo1029/items/8827d95327b647a6cf50

 

    • https://qiita.com/rykgy/items/2714299253bcc87e0118

 

    • https://github.com/tokuhirom/spring-vue-sample

 

    https://spring.io/guides/tutorials/spring-boot-kotlin/
广告
将在 10 秒后关闭
bannerAds