pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.liaoxiang</groupId>
<artifactId>spring-security</artifactId>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
其他需要用到的后面再添加
权限控制涉及到主要数据库表结构:

menu表为资源表,即用户能访问的资源,url为后台接口的url pattern,将来用于和具体的请求进行对比,path为前端页面路由路径,component为具体组件,登陆成功之后,根据该用户的角色信息,获取可以访问的资源菜单,渲染到页面
其他的表数据为测试数据,不再叙述
在注释掉springsecurity依赖的情况下,能够通过接口正常获取数据
在取消注释之后,再次访问,跳转到了一个登陆页面
用户名为:user ,密码:日志中输出的密码
成功获取数据
这里使用的是Spring Security为我们生成的用户名和密码,下面我们使用自己数据中的用户来登陆
新建一个service类
UserLoginService
,这个类要实现一个接口
UserDetailsService
,这是Spring Security处理用户登陆的一个接口,实现接口里的方法:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
这个方法接收一个用户名,然后返回一个实现了
UserDetails
接口的对象,下面看一下这个接口里定义的内容:
public interface UserDetails extends Serializable {
//用户权限(角色)的集合
Collection<? extends GrantedAuthority> getAuthorities();
//密码
String getPassword();
//用户名
String getUsername();
//账户是否未过期
boolean isAccountNonExpired();
//账户是否未锁定
boolean isAccountNonLocked();
//密码是否为过期
boolean isCredentialsNonExpired();
//账户是否激活
boolean isEnabled();
}
UserLoginService :
package com.liaoxiang.service;
import com.liaoxiang.mapper.UserMapper;
import com.liaoxiang.model.User;
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;
/**
* @auther Mr.Liao
* @date 2019/8/19 14:57
*/
@Service
public class UserLoginService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//如果User为null,就会抛出异常信息:UsernameNotFoundException
User user = userMapper.findUserByUserName(username);
return user;
}
}
当执行
loadUserByUsername()
后,返回一个
UserDetails
的实现类对象,Spring Security就会拿着上面这些信息去判断,密码是否正确,几个boolean返回值的方法是否为true,从而来确定是否让用户登陆,而这些用户的信息应该都是我们数据库中用户的信息,因此我们可以让自己的用户实体类来实现
UserDetails
接口:
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @auther Mr.Liao
* @date 2019/8/18 17:18
*/
@Data
@ToString
public class User implements UserDetails {
private Integer userId;
private String userName;
private String password;
private String name;
private List<Role> roles;
private boolean enabled;
/**
* 账户的权限信息
* @return
*/
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getRoleAuthority()));
}
return authorities;
}
@Override
public String getUsername() {
return userName;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
在处理用户的权限信息时,需要一个实现了
GrantedAuthority
接口的实现类对象,我们使用
SimpleGrantedAuthority
,它实现了
GrantedAuthority
,需要注意的是,在Spring Security中角色信息以
ROLE_
开头,因此在角色表中要使用注意角色的权限名称:
public final class SimpleGrantedAuthority implements GrantedAuthority {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String role;
public SimpleGrantedAuthority(String role) {
Assert.hasText(role, "A granted authority textual representation is required");
this.role = role;
}
@Override
public String getAuthority() {
return role;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof SimpleGrantedAuthority) {
return role.equals(((SimpleGrantedAuthority) obj).role);
}
return false;
}
@Override
public int hashCode() {
return this.role.hashCode();
}
@Override
public String toString() {
return this.role;
}
}
添加配置信息:
userDetailsService
的实现类,和密码加密解密工具类
BCryptPasswordEncoder
这个我们在启动类中new了一个添加到IOC容器,在添加用户时,加密了密码,登陆时需要用来解密数据库密码和登陆时输入的密码进行比较
package com.liaoxiang.config;
import com.liaoxiang.service.UserLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @auther Mr.Liao
* @date 2019/8/18 22:34
*/
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserLoginService userLoginService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userLoginService)
.passwordEncoder(bCryptPasswordEncoder);
}
}
再次通过浏览器访问获取数据,跳转到登陆页,此时输入用户名和密码就是添加用户时的用户名和密码