SpringBoot怎么整合SpringSecurityOauth2实现鉴权动态权限问题
准备spring-boot:2.1.4.RELEASEspring-security-oauth3:2.3.3.RELEASE(如果要使用源码,不要随意改动这个版本号,因为2.4往上的写法不一样了)mysql:5.7效果展示这边只用了postman做测试,暂时未使用前端页面来对接,下个版本角色菜单权限分配的会有页面的展示1、访问开放接口http://localhost:7000/open/hello2、不带token访问受保护接口http://localhost:7000/admin/user/info3
准备
spring-boot:2.1.4.RELEASE
spring-security-oauth3:2.3.3.RELEASE(如果要使用源码,不要随意改动这个版本号,因为2.4往上的写法不一样了)
mysql:5.7
效果展示
这边只用了postman做测试,暂时未使用前端页面来对接,下个版本角色菜单权限分配的会有页面的展示
1、访问开放接口http://localhost:7000/open/hello

2、不带token访问受保护接口http://localhost:7000/admin/user/info

3、登录后获取token,带上token访问,成功返回了当前的登录用户信息


实现
oauth3一共有四种模式,这边就不做讲解了,网上搜一搜,千篇一律
因为现在只考虑做单方应用的,所以使用的是密码模式。
后面会出一篇SpringCloud+Oauth3的文章,网关鉴权
讲一下几个点吧
1、拦截器配置动态权限

新建一个 MySecurityFilter类,继承AbstractSecurityInterceptor,并实现Filter接口
初始化,自定义访问决策管理器
@PostConstruct
public void init(){
super.setAuthenticationManager(authenticationManager);
super.setAccessDecisionManager(myAccessDecisionManager);
}自定义 过滤器调用安全元数据源
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.mySecurityMetadataSource;
}先来看一下自定义过滤器调用安全元数据源的核心代码
以下代码是用来获取到当前请求进来所需要的权限(角色)
/** * 获得当前请求所需要的角色 * @param object * @return * @throws IllegalArgumentException */ @Override public CollectiongetAttributes(Object object) throws IllegalArgumentException { String requestUrl = ((FilterInvocation) object).getRequestUrl(); if (IS_CHANGE_SECURITY) { loadResourceDefine(); } if (requestUrl.indexOf("?") > -1) { requestUrl = requestUrl.substring(0, requestUrl.indexOf("?")); } UrlPathMatcher matcher = new UrlPathMatcher(); List
再来看一下自定义访问决策管理器核心代码,这段代码主要是判断当前登录用户(当前登录用户所拥有的角色会在最后一项写到)是否拥有该权限角色
@Override public void decide(Authentication authentication, Object o, CollectionconfigAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes == null){ //属于白名单的,不需要权限 return; } Iterator iterator = configAttributes.iterator(); while (iterator.hasNext()){ ConfigAttribute configAttribute = iterator.next(); String needPermission = configAttribute.getAttribute(); for (GrantedAuthority ga: authentication.getAuthorities()) { if(needPermission.equals(ga.getAuthority())){ //有权限,可访问 return; } } } throw new AccessDeniedException("没有权限访问"); }
2、自定义鉴权异常返回通用结果
为什么需要这个呢,如果不配置这个,对于前端,后端来说都很难去理解鉴权失败返回的内容,还不能统一解读,废话不多说,先看看不配置和配置了的返回情况
(1)未自定义前,没有携带token去访问受保护的API接口时,返回的结果是这样的

(2)我们规定一下,鉴权失败的接口返回接口之后,变成下面这种了,是不是更利于我们处理和提示用户

好了,来看一下是在哪里去配置的吧
我们资源服务器OautyResourceConfig,重写下下面这部分的代码,来自定义鉴权异常返回的结果
大伙可以参考下这个https://www.yisu.com/article/131668.htm
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(authenticationEntryPoint) //token失效或没携带token时
.accessDeniedHandler(requestAccessDeniedHandler); //权限不足时
}
3、获取当前登录用户
第一种:使用JWT携带用户信息,拿到token后再解析
暂不做解释
第二种:写一个SecurityUser实现UserDetails接口(这个工程中使用的是这一种)
原来的只有UserDetails接口只有username和password,这里我们加上我们系统中的User
protected User user;
public SecurityUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}在BaseController,每个Controller都会继承这个的,在里面写给getUser()的方法,只要用户带了token来访问,我们可以直接获取当前登录用户的信息了
protected User getUser() {
try {
SecurityUser userDetails = (SecurityUser) SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
User user = userDetails.getUser();
log.debug("【用户:】:" + user);
return user;
} catch (Exception e) {
}
return null;
}那么用户登录成功后,如何去拿到用户的角色集合等呢,这里面就要实现UserDetailsService接口了

@Service
public class TokenUserDetailsService implements UserDetailsService{
@Autowired
private LoginService loginService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = loginService.loadUserByUsername(username); //这个我们拎出来处理
if(Objects.isNull(user))
throw new UsernameNotFoundException("用户名不存在");
return new SecurityUser(user);
}
}然后在我们的安全配置类中设置UserDetailsService为上面的我们自己写的就行

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}最后我们只需要在loginService里面实现我们的方法就好,根据我们的实际业务处理判断该用户是否存在等
@Override
public User loadUserByUsername(String username){
log.debug(username);
Map map = new HashMap();
map.put("username",username);
map.put("is_deleted",-1);
User user = userMapper.findByUsername(map);
if(user != null){
map = new HashMap();
map.put("user_id",user.getUser_id());
//查询用户的角色
List userRoles = userRoleMapper.findAll(map);
user.setRoles(listRoles(userRoles));
//权限集合
Collection extends GrantedAuthority> authorities = merge(userRoles);
user.setAuthorities(authorities);
return user;
}
return null;
} 数据库文件在这

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















