用最笨的方法填鸭式实现spirngboot+SpringSecurity
表关系

这里很简单 看起来乱
白话说就是一个用户 可以有很多角色 一个用户表 对应多个角色表
一个角色 可以有很多权限 一个角色表对应多个权限表
中间两个表为连接表 因为我们并没有外键直接指向对应表
所以我们就需要join 连接查询 类似通过角色表id查出连接表对应的字段
再从连接表对应的字段拿出角色id 匹配对应角色 巴拉巴拉的 你懂得
进入正题
依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
第一步骤
创建一个SecurityConfig 继承 WebSecurityConfigurerAdapter
重写里面
-
protected void configure(HttpSecurity http)
-
protected void configure(AuthenticationManagerBuilder auth)
两个方法
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private userDetailsServiceImpl userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
//定制请求的授权规则
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasAuthority("chuji")
.antMatchers("/level2/**").hasAuthority("zhongji")
.antMatchers("/level3/**").hasAuthority("gaoji");
//开启自动配置的登陆功能,效果,如果没有登陆,没有权限就会来到登陆页面
http.formLogin().usernameParameter("user").passwordParameter("pwd")
.loginPage("/userlogin");
//1、/login来到登陆页
//2、重定向到/login?error表示登陆失败
//3、更多详细规定
//4、默认post形式的 /login代表处理登陆
//5、一但定制loginPage;那么 loginPage的post请求就是登陆
//开启自动配置的注销功能。
http.logout().logoutSuccessUrl("/");//注销成功以后来到首页
//1、访问 /logout 表示用户注销,清空session
//2、注销成功会返回 /login?logout 页面;
//开启记住我功能
http.rememberMe().rememberMeParameter("remeber");
//登陆成功以后,将cookie发给浏览器保存,以后访问页面带上这个cookie,只要通过检查就可以免登录
//点击注销会删除cookie
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.userDetailsService(userDetailsService());
}
第二步
创建一个bean 类 也叫做model 来实现 UserDetails 至于为啥实现这个类看下去就知道了
//用户表对应实体类
public class SysUser implements Serializable {
private static final long serialVersionUID = -6525908145032868837L;
private Long id;
private String username;
private String password;
private String nickname;
private String headImgUrl;
private String phone;
private String telephone;
private String email;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
private Integer sex;
private Integer status;
private String intro;
实现UserDetails 类 这里是继承了用户表实体类
public class LoginUser extends SysUser implements UserDetails {
private List<Permission> list;
public List<Permission> getList() {
return list;
}
public void setList(List<Permission> list) {
this.list = list;
}
//权限列表
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return list.stream().filter(p->!StringUtils.isEmpty(p.getPermission())).map(p-> new SimpleGrantedAuthority(p.getPermission()))
.collect(Collectors.toList());
}
// 账户是否未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
// 账户是否未锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
// 密码是否未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 账户是否激活
@Override
public boolean isEnabled() {
return true;
}
可以看到这个类里面多了很多方法尤其是多了
public Collection<? extends GrantedAuthority> getAuthorities()
这个方法很重要使SpringSecurity 会自动调用拿出来里面的权限
可能使用流写法比较难懂毕竟我们都是菜鸟嘛
所以对应为for循环的代码就出现了
list.stream().filter(p->!StringUtils.isEmpty(p.getPermission())).map(p-> new SimpleGrantedAuthority(p.getPermission()))
.collect(Collectors.toList());
上面代码等同于下面代码
-------------------------------------------------------------------------------
HashSet<SimpleGrantedAuthority> set = new HashSet<SimpleGrantedAuthority>();
for (Permission p: list) {
if (p.getPermission() !=null) {
set.add(new SimpleGrantedAuthority(p.getPermission()));
}
}
第三部创建dao层
@Mapper
public interface PermissionDao {
/*
* 通过用户id获取权限
* */
@Select("select p.* from sys_permission p inner " +
"join sys_role_permission rp on p.id=rp.permissionId inner join sys_role_user ru on ru.roleId = rp.roleId where ru.userId = #{userId}")
List<Permission> listByUserId(Long userId);
}
@Mapper
public interface SysUserDao {
//通过账号获取用户
@Select("select * from sys_user t where t.username = #{username}")
SysUser getUser(String username);
}
第四步
创建一个 service 继承 UserDetailsService 框架需要来判断是否登录 相当于我们自己写的 判断登录service 不过框架给你实现了 收了框架的好 拿人手短
就得听从框架的规矩 实现 这个UserDetailsService
@Service
public class userDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserDao sysUserDao;
@Autowired
private PermissionDao permissionDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername");
//按照名字查询用户
SysUser sysUser = sysUserDao.getUser(username);
if (sysUser == null) {
throw new AuthenticationCredentialsNotFoundException("用户名不存在");
}
LoginUser loginUser = new LoginUser();
BeanUtils.copyProperties(sysUser, loginUser);
//通过用户id返回权限表
List<Permission> permissions = permissionDao.listByUserId(sysUser.getId());
loginUser.setList(permissions);
System.out.println("用户"+sysUser.toString());
System.out.println("权限列表"+permissions.toString());
return loginUser;
}
public UserDetails loadUserByUsername(String username)
在这里方法只有一个那就是 loadUserByUsername 但是注意返回的类型为UserDetails这像不像我们第二步实现的 UserDetails 类 正好返回他
看上面代码可以看出loadUserByUsername 的实现其实就是
- 通过username 账号取出我们的用户表user的信息
- 通过user的id取出我们的权限信息
- new了一个UserDetails 接口的实现类我们自己实现的类loginUse
- 把用户信息存入loginUse
- 把权限信息传入loginUse
- 返回给SpringSecurity
经过上面spring就拿到我们的权限信息和用户信息了 之后spirng会通过这些信息来进行密码比较 权限比较此时此刻我们可以说高枕无忧了
源码地址
[https://gitee.com/jia_yongli/springboot-05-security.git]
数据库文件
用户表测试数据

这两个为账号密码为测试数据其他为另外的项目
角色数据

权限数据

为null的都是用不到的字段
浙公网安备 33010602011771号