尝试使用Spring Boot实现身份验证功能
首先
我们将使用Spring Boot来构建Web应用程序环境。
这次我们将尝试创建一个需要身份验证的页面。
认证信息(用户名和密码)我们将在MySQL中进行实践性地管理。
继上一次之后,在Eclipse Neon上进行实现。
环境
-
- Windows 10
-
- Eclipse Neon
-
- Java 1.8.0_111
- MySQL 5.7
指定所需的模块。
可以使用Spring Security模块实现认证处理。
使用Spring Data JPA模块进行数据库连接。
由于使用了模板引擎(Thymeleaf)进行页面创建,所以需要加载这些模块。
在Eclipse中,您可以指定在创建新项目时加载的模块,但并不总是能从新项目开始。所以,这次我们直接将定义写入build.gradle文件。在build.gradle中有一个名为dependencies的部分,您可以这样进行设置。
dependencies {
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-devtools')
compile('mysql:mysql-connector-java')
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.5'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
数据库连接的设置
接下来是数据库连接的设置。
请将其配置在 application.properties 文件中。如有需要,请同时写入端口号。
spring.datasource.url=jdbc:mysql://[MySQL のサーバ名]/[データベース名]
spring.datasource.username=[ユーザ名]
spring.datasource.password=[パスワード]
spring.datasource.driverClassName=com.mysql.jdbc.Driver
实施
下面是翻译的方式:
准备工作做好了,接下来就是开始实施了。
我们将实施一个机制,即在表单验证成功后显示首页。
如果未进行验证,将显示登录页面。
访问成员信息
在实现访问数据库中的成员信息的处理之前,需要先创建一个用于管理成员信息的数据库表。
create table member(
id BIGINT auto_increment,
username VARCHAR(20) NOT NULL,
password VARCHAR(20) NOT NULL,
unique index (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO member (username, password)
VALUES ('user', 'pass')
;
由于这个源码只是一个样本,所以我们会以明文形式保存密码,但如果是真实数据的话,这样的实现方式是不可取的。
准备实体类
准备与已创建的表对应的实体类。因为它用于身份验证信息,所以我们将继承UserDetails类。这个类带有各种方法。根据方法名可以看出,它们返回的是锁定状态等信息,但是这次我们将统一返回true。
package com.example.entity;
import java.util.Collection;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
@Table(name = "member")
public class MemberEntity implements UserDetails
{
private static final long serialVersionUID = 1667698003975566301L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
return null;
}
@Override
public String getPassword()
{
return this.password;
}
@Override
public String getUsername()
{
return this.username;
}
@Override
public boolean isAccountNonExpired()
{
return true;
}
@Override
public boolean isAccountNonLocked()
{
return true;
}
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
@Override
public boolean isEnabled()
{
return true;
}
}
创建存储库接口和服务
创建一个用于获取成员信息的类(接口)。
package com.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.entity.MemberEntity;
public interface MemberRepository extends JpaRepository<MemberEntity, Long>
{
public MemberEntity findByUsername(String username);
}
此外,我们将创建一个调用存储库的服务类。
package com.example.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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;
import com.example.entity.MemberEntity;
import com.example.repository.MemberRepository;
@Service
public class MemberServiceImpl implements UserDetailsService
{
private MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
if (StringUtils.isEmpty(username))
{
throw new UsernameNotFoundException("");
}
MemberEntity memberEntity = memberRepository.findByUsername(username);
if (memberEntity == null)
{
throw new UsernameNotFoundException("");
}
return memberEntity;
}
@Autowired
public void setMemberRepository(MemberRepository memberRepository)
{
this.memberRepository = memberRepository;
}
}
创建首页页面 (Chuangjian shouye ye mian)
我們將創建一個需要身份認證的頁面。
package com.example.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class TopController
{
public static final String PAGE = "/";
private static final String HTML = "top";
@RequestMapping(value = TopController.PAGE, method = RequestMethod.GET)
public String top(Model model)
{
return TopController.HTML;
}
}
我也会创建模板文件,并将其放置在templates目录下。
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
</head>
<body>
こんにちは!
<form action="#" th:action="@{/logout}" method="POST">
<input type="submit" value="ログアウト" />
</form>
</body>
</html>
设置访问权限
创建一个配置类,对访问权限进行设置。
将配置信息写入源代码是现代的做法!
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception
{
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
@Autowired
public void setUserDetailsService(UserDetailsService userDetailsService)
{
this.userDetailsService = userDetailsService;
}
}
确认动作
运行>运行为>以Spring Boot App方式启动。
启动后,请在浏览器中访问以下URL。
请问一切顺利吗?已经动起来了吗?
可以进行各种自定义。
登录页面
到目前为止,我们使用了标准的登录页面,但当然可以自己创建。
准备登录页面的控制器和模板…
package com.example.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController
{
public static final String PAGE = "/login";
private static final String HTML = "login";
@RequestMapping(value = LoginController.PAGE)
public String top(Model model)
{
return LoginController.HTML;
}
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>ログイン</title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="" th:action="@{/login}" method="post">
<p>
ユーザーID:
<input type="text" name="user" />
</p>
<p>
パスワード:
<input type="password" name="pass" />
</p>
<p>
<input type="submit" value="ログイン" />
</p>
</form>
<div th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null">
<span th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}"></span>
</div>
</body>
</html>
请添加设置。
在定义中有一个参数的位置来写用户名和密码,请确保与HTML一致。
package com.example.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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 com.example.web.LoginController;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity.authorizeRequests().anyRequest().authenticated();
httpSecurity.formLogin().loginPage(LoginController.PAGE).usernameParameter("user")
.passwordParameter("pass").permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception
{
authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}
@Autowired
public void setUserDetailsService(UserDetailsService userDetailsService)
{
this.userDetailsService = userDetailsService;
}
}
稍微解释一下
春季安全
Spring Security 是一个用于实施安全措施的模块。本次我们实现了认证功能,但它还具有许多其他功能,例如CSRF防护等。认证功能还支持BASIC认证,并且可以进行自定义,例如指定认证排除的路径。
春季数据 JPA
Spring Data JPA 是一个模块,它可以在不编写 SQL 的情况下访问数据库。基本操作(在这种情况下是获取与用户名对应的记录)只需定义一个方法即可完成。非常方便!
由于这是一个深奥的领域,我建议您详细搜索一下……但是请注意,搜索结果可能包括台球协会、滑翔伞协会、举重协会等信息,所以请小心。
请参考
-
- GETTING STARTED Securing a Web Application
-
- Spring SecurityでWebの認証と認可を制御する
- Spring BootとSpring Securityでユーザ認証(インメモリ&独自のユーザテーブル)