写在前面

思考:为什么需要鉴权呢?

系统开发好上线后,API接口会暴露在互联网上会存在一定的安全风险,例如:爬虫、恶意访问等。因此,我们需要对非开放API接口进行用户鉴权,鉴权通过之后再允许调用。

准备

spring-boot:2.1.4.RELEASE

spring-security-oauth2: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访问,成功返回了当前的登录用户信息

实现

oauth2一共有四种模式,这边就不做讲解了,网上搜一搜,千篇一律

因为现在只考虑做单方应用的,所以使用的是密码模式。

后面会出一篇SpringCloud+Oauth2的文章,网关鉴权

讲一下几个点吧

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 Collection<ConfigAttribute> getAttributes(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<Object> list = new ArrayList<>(); //无需权限的,直接返回
list.add("/oauth/**");
list.add("/open/**");
if(matcher.pathsMatchesUrl(list,requestUrl))
return null; Set<String> roleNames = new HashSet();
for (Resc resc: resources) {
String rescUrl = resc.getResc_url();
if (matcher.pathMatchesUrl(rescUrl, requestUrl)) {
if(resc.getParent_resc_id() != null && resc.getParent_resc_id().intValue() == 1){ //默认权限的则只要登录了,无需权限匹配都可访问
roleNames = new HashSet();
break;
}
Map map = new HashMap();
map.put("resc_id", resc.getResc_id());
// 获取能访问该资源的所有权限(角色)
List<RoleRescDTO> roles = roleRescMapper.findAll(map);
for (RoleRescDTO rr : roles)
roleNames.add(rr.getRole_name());
}
} Set<ConfigAttribute> configAttributes = new HashSet();
for(String roleName:roleNames)
configAttributes.add(new SecurityConfig(roleName)); log.debug("【所需的权限(角色)】:" + configAttributes); return configAttributes;
}

再来看一下自定义访问决策管理器核心代码,这段代码主要是判断当前登录用户(当前登录用户所拥有的角色会在最后一项写到)是否拥有该权限角色

@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(configAttributes == null){ //属于白名单的,不需要权限
return;
}
Iterator<ConfigAttribute> 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://blog.csdn.net/Pastxu/article/details/124538364

@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<UserRoleDTO> userRoles = userRoleMapper.findAll(map);
user.setRoles(listRoles(userRoles));
//权限集合
Collection<? extends GrantedAuthority> authorities = merge(userRoles);
user.setAuthorities(authorities);
return user;
}
return null; }

 

大功告成啦,赶紧动起手来吧!

附上源码地址:https://gitee.com/jae_1995/spring-boot-oauth2

数据库文件在这

点个小赞呗

SpringBoot整合SpringSecurityOauth2实现鉴权-动态权限的更多相关文章

  1. SpringBoot整合SpringSecurity示例实现前后分离权限注解

    SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证 作者:Sans_ juejin.im/post/5da82f066fb9a04e2a73daec 一.说 ...

  2. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

  3. SpringBoot整合spring-security-oauth2完整实现例子

    SpringBoot整合spring-security-oauth2完整实现例子 技术栈 : springboot + spring-security + spring-oauth2 + mybati ...

  4. SpringBoot系列: Web应用鉴权思路

    ==============================web 项目鉴权============================== 主要的鉴权方式有:1. 用户名/密码鉴权, 然后通过 Sess ...

  5. SpringBoot使用token简单鉴权

    本文使用SpringBoot结合Redis进行简单的token鉴权. 1.简介 刚刚换了公司,所以最近有些忙碌,所以一直没有什么产出,最近朋友问我登录相关的,所以这里先写一篇简单使用token鉴权的文 ...

  6. springboot整合security实现基于url的权限控制

    权限控制基本上是任何一个web项目都要有的,为此spring为我们提供security模块来实现权限控制,网上找了很多资料,但是提供的demo代码都不能完全满足我的需求,因此自己整理了一版. 在上代码 ...

  7. Springboot 整合ApachShiro完成登录验证和权限管理

    1.前言 做一个系统最大的问题就是安全问题以及权限的问题,如何正确的选择一个安全框架对自己的系统进行保护,这方面常用的框架有SpringSecurity,但考虑到它的庞大和复杂,大多数公司还是会选择 ...

  8. Spring Security入门(3-6)Spring Security 的鉴权 - 自定义权限前缀

  9. 凭借SpringBoot整合Neo4j,我理清了《雷神》中错综复杂的人物关系

    原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. 虽然距离中秋放假还要熬过漫长的两天,不过也有个好消息,今天是<雷神4>上线Disney+流媒体的日子 ...

随机推荐

  1. Linux操作系统与项目部署

    Linux操作系统与项目部署 注意:本版块会涉及到操作系统相关知识. 现在,几乎所有智能设备都有一个自己的操作系统,比如我们的家用个人电脑,基本都是预装Windows操作系统,我们的手机也有Andro ...

  2. layui下拉框渲染问题,以及回显问题

    最近实习公司给的新人练手项目用的layui,layui之前自己也接触过但是也就是用了用table组件,没有用过layer弹层这些东西,所以就了解了一下. 首先遇到的一个问题就是下拉框没有样式,然后加样 ...

  3. Azure DevOps (十) 通过流水线完成Docker镜像的部署

    上一篇文章中,我们通过azure的流水线完成了镜像推送到镜像仓库中去,本篇文章我们继续开始完成下一步,通过流水线把镜像从仓库拉取到任意一台公网的服务器上去, 完成镜像部署的闭环. 首先我们需要先准备一 ...

  4. [.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢?

    [.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢? 在ASP.NET Core应用程序中,接 ...

  5. 新零售SaaS架构:组织管理的底层逻辑与架构设计

    想要深入理解零售企业的组织架构,是非常困难的一件事.因为大部分人都没有实际经营过一家零售企业,更没有参与设计过零售企业的组织架构. 调研商家时,我们只能了解商家组织架构的现状,我们也很难和企业高层直接 ...

  6. [AcWing 771] 字符串中最长的连续出现的字符

    点击查看代码 #include<iostream> using namespace std; string str; int n; int main() { cin >> n; ...

  7. 【PyTorch】常用的神经网络层汇总(持续补充更新)

    1. Convolution Layers 1.1 nn.Conv2d (1)原型 torch.nn.Conv2d(in_channels, out_channels, kernel_size, st ...

  8. 以rem为单位,数值较小,border-radius:50%部分浏览器渲染不圆方法

    元素使用rem做单位且较小时,对于border-radius:50%在部分浏览器不圆解决方法: 1.将原来宽高扩大至两倍(.1rem --> .2rem),再使用transform:scale( ...

  9. Hive 3.x 配置&详解

    Hive 1. 数据仓库概述 1.1 基本概念 数据仓库(英语:Data Warehouse,简称数仓.DW),是一个用于存储.分析.报告的数据系统. 数据仓库的目的是构建面向分析的集成化数据环境,分 ...

  10. 167. Two Sum II - Input array is sorted - LeetCode

    Question 167. Two Sum II - Input array is sorted Solution 题目大意:和Two Sum一样,这里给出的数组是有序的 思路:target - nu ...