在第七章的基础上进行开发
一、建立权限(角色)的实体
二、建立用户和权限的关系
三、创建用户时,关联角色
四、修改用户的角色
五、初始化权限(角色)、用户的数据
首先在项目的pom.xml文件中添加如下依赖
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--对Thymeleaf添加Spring Security标签支持-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>
权限实体 Authority 实现GrantedAuthority接口,实现getAuthority方法来替换getName方法
package com.waylau.spring.boot.blog.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.springframework.security.core.GrantedAuthority;
/**
* 权限实体
* @author Administrator
*
*/
@Entity
public class Authority implements GrantedAuthority{
private static final long serialVersionUID = 1L;
@Id//主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键的生成策略(自增)
private Long id;//实体一个唯一标识
@Column(nullable = false)//映射为字段 值不能为空
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override//覆盖getName方法
public String getAuthority() {
// TODO Auto-generated method stub
return name;
}
public void setName(String name) {
this.name = name;
}
}
对原来的User实体类做修改 实现UserDetails接口
package com.waylau.spring.boot.blog.domain;
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.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.validation.constraints.Size;
import javax.persistence.JoinColumn;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 用户实体
* @author Administrator
* Spring注解 @NotBlank,@NotNull,@NotEmpty
*/
@Entity//实体
public class User implements UserDetails{
private static final long serialVersionUID = 1L;
@Id//主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键的生成策略(自增)
private Long id;//实体一个唯一标识
@NotEmpty(message = "姓名不能为空")//不能为null,也不能为空
@Size(min = 2, max = 20)
@Column(nullable = false, length = 20)//映射为字段 值不能为空
private String name;
@NotEmpty(message = "邮箱不能为空")
@Size(max = 50)
//nullable表示该字段是否可以为null值,默认为true unique表示该字段是否为唯一标识,默认为false
@Email(message = "邮箱格式不对")
@Column(nullable = false, length = 50, unique = true)
private String email;
@NotEmpty(message = "账号不能为空")
@Size(min = 3, max = 20)
@Column(nullable = false, length = 20, unique = true)
private String username;//用户账号 用户登录的唯一标识
@NotEmpty(message = "密码不能为空")
@Size(max = 100)
@Column(length = 100)//字段长度
private String password;//密码
@Column(length = 200)
private String avatar;//头像信息
/**
* CascadeType.DETACH 级联脱管/游离操作。如果你要删除一个实体,但是它有外键无法删除,你就需要这个级联权限了。它会撤销所有相关的外键关联
* FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载
* @JoinTable name属性为连接两个表的表名称(即中间表)。若不指定,则使用默认的表名称如下所示 “表名1”+“_”+“表名2”
* joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id") user_id为外键关联到user表中的id字段
* inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id") authority_id为外键关联到authority表中的id字段
* 注意:@JoinColumn 要手动导入
*/
@ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)//多对多关系
@JoinTable(name = "user_authority", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id"))
private List<Authority> authorities;//用户和权限的关系
protected User(){}//无参构造 设为protected防止直接使用
public User(Long id, String name, String username, String email) {
this.id = id;
this.name = name;
this.email = email;
this.username = username;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", email=" + email + ", username=" + username + ", password="
+ password + ", avatar=" + avatar + "]";
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 需将 List<Authority> 转成 List<SimpleGrantedAuthority>,否则前端拿不到角色列表名称
List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();
for(GrantedAuthority authority : this.authorities){
simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
return simpleAuthorities;
}
public void setAuthorities(List<Authority> authorities){
this.authorities = authorities;
}
//账号未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账号未锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//凭证未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//是否启用
@Override
public boolean isEnabled() {
return true;
}
}
AuthorityRepository如下
package com.waylau.spring.boot.blog.domain;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Authority 仓库
* @author Administrator
*
*/
public interface AuthorityRepository extends JpaRepository<Authority, Long>{
}
服务接口AuthorityService
package com.waylau.spring.boot.blog.service;
import com.waylau.spring.boot.blog.domain.Authority;
/**
* Authority 服务接口
* @author Administrator
*
*/
public interface AuthorityService {
/**
* 根据ID查询Authority
* @param id
* @return
*/
Authority getAuthorityById(Long id);
}
服务实现类 AuthorityServiceImpl
package com.waylau.spring.boot.blog.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.waylau.spring.boot.blog.domain.Authority;
import com.waylau.spring.boot.blog.domain.AuthorityRepository;
/**
* Authority 服务接口的实现
* @author Administrator
*
*/
@Service
public class AuthorityServiceImpl implements AuthorityService{
@Autowired
private AuthorityRepository authorityRepository;
@Override
public Authority getAuthorityById(Long id) {
return authorityRepository.findOne(id);
}
}
修改UserController 自动注入
@Autowired
private AuthorityService authorityService;
修改方法如下
/**
* 保存或修改用户
* @param user
* @return
*/
@PostMapping
public ResponseEntity<Response> saveOrUpdateUser(User user, Long authorityId){
List<Authority> authorities = new ArrayList<Authority>();
authorities.add(authorityService.getAuthorityById(authorityId));
user.setAuthorities(authorities);
try{
userService.saveOrUpdateUser(user);
} catch (ConstraintViolationException e) {
return ResponseEntity.ok().body(new Response(false, ConstraintViolationExceptionHandler.getMessage(e)));
}
return ResponseEntity.ok().body(new Response(true, "处理成功", user));
}
修改MainController 注册方法
private static final Long ROLE_USER_AUTHORITY_ID = 2L;//注册用户是博主的权限
@Autowired
private AuthorityService authorityService;
/**
* 注册用户
* @param user
* @return
*/
@PostMapping("/register")
public String registerUser(User user){
//赋予权限
List<Authority> authorities = new ArrayList<Authority>();
authorities.add(authorityService.getAuthorityById(ROLE_USER_AUTHORITY_ID));
user.setAuthorities(authorities);
userService.registerUser(user);
return "redirect:/login";//成功后跳转到登录页面
}
安全配置类如下
package com.waylau.spring.boot.blog.config;
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;
/**
* 安全配置类
*/
@EnableWebSecurity//启用web安全
public class SecurityConfig extends WebSecurityConfigurerAdapter{
/**
* 自定义配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll();//都可以访问
}
}
前端修改三个页面
list.html中添加权限的显示
在add.html中添加(因为固定设置了两种角色 管理员和博主)
<div class="form-group">
<label for="authorities" class="col-form-label">角色</label>
<select id="authorities" name="authorityId" class="form-control form-control-chosen" data-placeholder="请选择">
<option value="1">管理员</option>
<option value="2">博主</option>
</select>
</div>
在edit.html中添加
<div class="form-group">
<label for="authorities" class="col-form-label">角色</label>
<select id="authorities" name="authorityId" class="form-control form-control-chosen" data-placeholder="请选择">
<option value="1" data-th-selected="${#strings.contains(userModel.user.authorities[0],'ROLE_ADMIN')}">管理员</option>
<option value="2" data-th-selected="${#strings.contains(userModel.user.authorities[0],'ROLE_USER')}">博主</option>
</select>
</div>
初始化数据(采用脚本)
当启动服务的时候会自动读取import.sql
INSERT INTO user (id, username, password, name, email) VALUES (1, 'admin', '123456', '老卫', 'i@waylau.com');
INSERT INTO user (id, username, password, name, email) VALUES (2, 'waylau', '123456', 'Way Lau', 'waylau@waylau.com');
INSERT INTO authority (id, name) VALUES (1, 'ROLE_ADMIN');
INSERT INTO authority (id, name) VALUES (2, 'ROLE_USER');
INSERT INTO user_authority (user_id, authority_id) VALUES (1, 1);
INSERT INTO user_authority (user_id, authority_id) VALUES (2, 2);
数据库如下
关联表的外键设置
测试页面如下 http://localhost:8080/admins
执行添加功能(修改功能类似)
结果如下
测试注册功能
注册完成后会跳转到登录页面,此时我们直接查看数据/admins
默认注册的用户赋予了博主的权限,也就是常量
private static final Long ROLE_USER_AUTHORITY_ID = 2L;//注册用户是博主的权限