Spring Security 提供多种认证方式:

 HTTP BASIC authentication headers (一个基于 IEFT RFC 的标准)
 HTTP Digest authentication headers (一个基于 IEFT RFC 的标准)
 HTTP X.509 client certificate exchange (一个基于 IEFT RFC 的标准)
 LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)
 Form-based authentication (提供简单用户接口的需求)
 OpenID authentication

 基于预先建立的请求头进行认证 (比如 Computer Associates Siteminder)
 JA-SIG Central Authentication Service (也被称为 CAS,这是一个流行的
开源单点登录系统)
 Transparent authentication context propagation for Remote Method
Invocation (RMI) and HttpInvoker (一个 Spring 远程调用协议)
 Automatic “remember-me” authentication (这样你可以设置一段时间,避
免在一段时间内还需要重新验证)
 Anonymous authentication (允许任何调用,自动假设一个特定的安全主体)
 Run-as authentication (这在一个会话内使用不同安全身份的时候是非常有 用的)
 Java Authentication and Authorization Service (JAAS)
 JEE Container autentication (这样,你可以继续使用容器管理认证,如果想
的话)
 Kerberos
 Java Open Source Single Sign On (JOSSO) *
 OpenNMS Network Management Platform *
 AppFuse *
 AndroMDA *
 Mule ESB *
 Direct Web Request (DWR) *
 Grails *
 Tapestry *
 JTrac *
 Jasypt *
 Roller *
 Elastic Plath *
 Atlassian Crowd *
。。。。。

本示例以表单登录为例,来演示自定义表单登录:

1. 使用版本及工具

Spring Boot 1.5.9.RELEASE
JDK1.8
Maven 3
Eclipse
MySQL

2. 示例结构

3. 定义用户信息和权限

通过实现 UserDetails 借口来定义用户表,UserInfo.java 用以存储用户基本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package com.devnp.springbootsecurityformlogindemo.entity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
* @author duliu
* 用户信息
*/
@Entity
@Table(name="user_info")
public class UserInfo implements UserDetails {

/**
*
*/
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue
private Long id ;

@Column(name="user_name", length=30)
private String userName ;

@Column(name="password", length=256)
private String password ;

@OneToMany(fetch = FetchType.EAGER, mappedBy="userInfo", cascade=CascadeType.ALL)
private List<UserRole> userRoles = new ArrayList<>();

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
List<GrantedAuthority> authorities = new ArrayList<>();

for (UserRole userRole : userRoles) {
authorities.add(new SimpleGrantedAuthority(userRole.getSysRole().getRoleName()));
}

return authorities;
}

@Override
public String getPassword() {
// TODO Auto-generated method stub
return this.password;
}

@Override
public String getUsername() {
// TODO Auto-generated method stub
return this.userName;
}

@Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return true;
}

@Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return true;
}

@Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return true;
}

@Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return true;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public void setPassword(String password) {
this.password = password;
}

}

SysRole.java 系统角色权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.devnp.springbootsecurityformlogindemo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
*
* 用户权限角色定义:
*
* 1.超级管理员 初始化账号
*
* 2.普通管理员
*
* @author duliu
*
* 系统权限表
*/
@Entity
@Table(name="sys_role")
public class SysRole {

@Id
@GeneratedValue
@Column(name="role_id")
private Long roleId;

@Column(name="role_name", length=30)
private String roleName ;


public SysRole() {
super();
}

public SysRole(Long roleId, String roleName) {
super();
this.roleId = roleId;
this.roleName = roleName;
}

public Long getRoleId() {
return roleId;
}

public void setRoleId(Long roleId) {
this.roleId = roleId;
}

public String getRoleName() {
return roleName;
}

public void setRoleName(String roleName) {
this.roleName = roleName;
}

}

UserRole.java 用户具有的角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.devnp.springbootsecurityformlogindemo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
*
* @author duliu
* 用户具有的权限
*/
@Entity
@Table(name="user_role")
public class UserRole {

@Id
@GeneratedValue
@Column(name="id")
private Long id;

@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private UserInfo userInfo ;

@JsonIgnore
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "role_id", nullable = false)
private SysRole sysRole ;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public UserInfo getUserInfo() {
return userInfo;
}

public void setUserInfo(UserInfo userInfo) {
this.userInfo = userInfo;
}

public SysRole getSysRole() {
return sysRole;
}

public void setSysRole(SysRole sysRole) {
this.sysRole = sysRole;
}

}

4. UserDetailsService

在Spring Security 中 通过实现 UserDetailsService 接口来完成对自定义用户信息的查找:

UserInfoService.java

1
2
3
4
5
6
7
8
9
10
11
package com.devnp.springbootsecurityformlogindemo.service;

import org.springframework.security.core.userdetails.UserDetailsService;

/**
* @author duliu
*
*/
public interface UserInfoService extends UserDetailsService{

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.devnp.springbootsecurityformlogindemo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.devnp.springbootsecurityformlogindemo.repository.UserInfoRepository;

/**
* @author duliu
*
*/
@Service
public class UserInfoServiceInpl implements UserInfoService {

@Autowired
private UserInfoRepository userInfoRepository;


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
return userInfoRepository.findByUserName(username);
}

}

5. Repository

UserInfoRepository.java 同过Spring JPA 来完成查找:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.devnp.springbootsecurityformlogindemo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.devnp.springbootsecurityformlogindemo.entity.UserInfo;

/**
* @author duliu
*
*/
@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {

UserInfo findByUserName(String userName);
}

6. 配置

首先对表单登录的配置:
1.指定表单登录
2.设置登录的URL/页面
3.设置登录发送的URL
4.以及对登录成功后处理和跳转
5.配置“/”为不需要登录验证
6.设置其他请求都需要授权

SecurityFormLoginConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.devnp.springbootsecurityformlogindemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
* @author duliu
*
* Spring Security 配置
*
*/
@Configuration
public class SecurityFormLoginConfig extends WebSecurityConfigurerAdapter{

/**
* 配置密码处理,对用户密码进行加密,验证
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
//.successForwardUrl("")
//.successHandler(null)
.and()
.authorizeRequests().antMatchers("/").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable();
}

}

对系统常用配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
context-path: /boot-form-login

spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
username: root
password: "!qaz2wsx"
jpa:
hibernate:
ddl-auto: update
show-sql: true

7. 登录页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Login Form</title>
</head>
<body>
<form action="login" method="POST">
<div>
<div>
<label>用户名:</label><input type="text" name="username" />
</div>

<div>
<label>密 码:</label><input name="password" type="password"/>
</div>
</div>

<div>
<button type="submit">登陆</button>
</div>

</form>
</body>
</html>

8. 控制器

其控制器主要包括跳转登录页面,以及访问登录者信息:

IndexController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

package com.devnp.springbootsecurityformlogindemo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* @author duliu
*
*/
@Controller
public class IndexController {

@RequestMapping("/")
public String index() {

return "login-form" ;
}

@RequestMapping("/index")
@ResponseBody
public Object welcome(@Autowired Authentication authentication) {

return authentication ;
}
}

9. 测试

可以同过执行如下SQL来插入一个用户:

1
2
3
insert into user_info (user_name, password) values ('duliu','$2a$10$MraEjVZI8V8v4gsykjjFxeRxZa.HiNIsPtueOLXb0ekuegSRDyW26') ;
insert into sys_role (role_name) values ('admin') ;
insert into user_role (user_id, role_id) values (1,1) ;

访问 http://localhost:8080/boot-form-login/index 跳转到登录页面:

输入用户名 和 密码 登录:

10. 代码下载

spring-boot-security-form-login-demo.zip