1、自定义权限认证的实现思路
-
自定义一个实体类LoginUser 用来保存SygUser 用户信息以及权限信息Set<String> permissions -
让这个LoginUser 实体类实现Userdetails 接口 -
自定义的MyUserDetailsServiceImpl 类的loadUserByUsername() 方法中实现用户信息查询及该用户的权限信息查询 -
自定义的MyUserDetailsServiceImpl 类的loadUserByUsername() 方法返回LoginUser 实体类对象
2、代码实现以及中间出现的问题的解决方法
2.1 定义LoginUser实体类【初始定义,一会还会儿修改】
自定义登录用户实体类LoginUser 如下,让其实现UserDetails 接口
package com.yige.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
public class LoginUser implements UserDetails{
private Set<String> permissions;
private SygUser user;
public LoginUser() {
}
public LoginUser(SygUser user, Set<String> permissions) {
this.permissions = permissions;
this.user = user;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SygUser getUser() {
return user;
}
public void setUser(SygUser user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@JsonIgnore
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return null;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return false;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return false;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return false;
}
}
2.2 修改MyUserDetailsServiceImpl类
修改loadUserByUsername(String userName) 方法的实现逻辑,让其最后返回LoginUser 实体类对象,具体实现如下
package com.yige.service.impl;
import com.yige.pojo.LoginUser;
import com.yige.pojo.SygUser;
import com.yige.service.SygUserSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SygUserSerivce userSerivce;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
if(!StringUtils.hasLength(userName)) throw new UsernameNotFoundException("用户名不能为空");
SygUser sygUser = userSerivce.selectUserByUserName(userName);
if(null == sygUser) throw new UsernameNotFoundException("用户不存在");
return createLoginUser(sygUser, new HashSet<String>());
}
public UserDetails createLoginUser(SygUser user, Set<String> permissions) {
return new LoginUser(user, permissions);
}
}
保存修改,重启项目,进行测试;
2.3 测试中遇到的问题及处理方法
使用账号student001 ,密码student001 进行正常登录测试, 发现报错User account is locked , 这肯定不对,我们的账号是在数据库查出来的,状态肯定是正常启用的,没有被锁定,这是怎么回事?先说说怎么解决,后面再说为什么会这样
2.3.1 User account is locked异常
`User account is locked异常解决方法
修改你的实体类LoginUser 的isAccountNonLocked 方法返回值为true 即可 修改之后 重新启动项目,再次测试
2.3.2 User is disabled异常
`User is disabled异常解决方法
修改你的实体类LoginUser 的isEnabled 方法返回值为true 即可 重新启动项目,再次测试
2.3.3 User account has expired异常
`User account has expired异常解决方法
修改你的实体类LoginUser 的isAccountNonExpired 方法返回值为true 即可 重新启动项目,再次测试
2.3.4 Bad credentials异常
Bad credentials异常解决方法
修改你的实体类LoginUser 的isCredentialsNonExpired 方法返回值为true 即可 重新启动项目,再次测试,
2.3.5 Empty encoded password异常
发现还是报错Bad credentials ,但是后台console打印了一条warn 日志,说是Empty encoded password ,说明我们提供的userDetails的实现类实现的getPassword方法无法正常获取到用户密码, 怎么处理? Empty encoded password异常解决方法
修改你的实体类LoginUser 的getPassword 方法返回值为你数据库用户实体类对象的密码即可 重新启动项目,再次测试 使用账号student001 ,密码student001 进行正常登录测试, 登录成功 使用账号manager ,密码manager 进行正常登录测试, 登录成功, OK, 基本流程走通
2.4 所有问题处理完成之后的LoginUser实体类的全貌
package com.yige.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.Set;
public class LoginUser implements UserDetails {
private Set<String> permissions;
private SygUser user;
public LoginUser() {
}
public LoginUser(SygUser user, Set<String> permissions) {
this.permissions = permissions;
this.user = user;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public SygUser getUser() {
return user;
}
public void setUser(SygUser user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@JsonIgnore
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return true;
}
}
2.5 我是怎么知道修改哪些方法就能解决问题的?
其实一开始我也不知道怎么处理这些问题, 我也觉得很奇怪, 为什么就会报这种错误? 直到我想到LoginUser实现了UserDetails接口, 里面有很多需要子类去实现的方法, 我就去看看了一下UserDetails接口中关于这几个方法的注释, 一下就明白了, 我们一起看看 以上就是为什么要修改LoginUser实现类那几个重写方法的返回值的原因, 不知道我有没有说清楚,哈哈, 个人理解哈,不喜勿喷~~
3、自定义权限的代码实现
3.1 实现查询用户权限的接口逻辑
package com.yige.service;
import com.yige.pojo.SygUser;
import java.util.HashSet;
import java.util.Set;
public interface SygPermissionService {
public Set<String> getMenuPermission(SygUser user);
}
package com.yige.service.impl;
import com.yige.mapper.SygPermissionMapper;
import com.yige.pojo.SygUser;
import com.yige.service.SygPermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Set;
@Service
public class SygPermissionServiceImpl implements SygPermissionService {
@Autowired
private SygPermissionMapper permissionMapper;
public Set<String> getMenuPermission(SygUser user) {
Set<String> perms = new HashSet<>();
perms.addAll(permissionMapper.selectMenuPermsByUserId(user.getUserId()));
return perms;
}
}
package com.yige.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface SygPermissionMapper {
List<String> selectMenuPermsByUserId(@Param("userId") Long userId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yige.mapper.SygPermissionMapper">
<select id="selectMenuPermsByUserId" resultType="string">
select
bm.menu_perms
from
bs_user as bu
left join
bs_user_role as bur
on
bu.user_id = bur.role_id
left join
bs_role_permission as brp
on
bur.role_id = brp.role_id
left join
bs_menu as bm
on
bm.menu_id = brp.menu_id
where
bu.user_id = #{userId}
</select>
</mapper>
3.2 修改MyUserDetailsServiceImpl类的实现逻辑
package com.yige.service.impl;
import com.yige.pojo.LoginUser;
import com.yige.pojo.SygUser;
import com.yige.service.SygPermissionService;
import com.yige.service.SygUserSerivce;
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 org.springframework.util.StringUtils;
import java.util.Set;
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SygUserSerivce userSerivce;
@Autowired
private SygPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
if(!StringUtils.hasLength(userName)) throw new UsernameNotFoundException("用户名不能为空");
SygUser sygUser = userSerivce.selectUserByUserName(userName);
if(null == sygUser) throw new UsernameNotFoundException("用户不存在");
return createLoginUser(sygUser, permissionService.getMenuPermission(sygUser));
}
public UserDetails createLoginUser(SygUser user, Set<String> permissions) {
permissions.stream().forEach(System.out::println);
return new LoginUser(user, permissions);
}
}
重新启动项目,再次测试 使用账号student001 ,密码student001 进行正常登录测试, 登录成功 控制台打印用户student001 的权限列表 使用账号manager ,密码manager 进行正常登录测试, 登录成功 控制台打印manager 用户的权限列表 至此,自定义权限认证的逻辑就全部实现完成了, 起始回头自己在捋一捋, 会不会发现其实也不太复杂,流程很清晰,实现很简单。喜欢我的文章请多多关注哈, 大家一起进步~
4、下一篇主要内容
下一篇主要研究一下WebSecurityConfigurerAdapter这个配置类以及 自定义配置类SecurityConfig来演示一下Spring security提供的各个接口的的作用以及怎么使用,敬请关注~
|