从数据库查询权限信息与自定义失败处理
Posted 生产队的大驴and小乌贼来了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从数据库查询权限信息与自定义失败处理相关的知识,希望对你有一定的参考价值。
从数据库查询权限信息
代码实现
我们只需要根据用户id去查询到其所对应的权限信息即可。
所以我们可以先定义个mapper,其中提供一个方法可以根据userid查询权限信息。
package com.example.qinghuatokendemo.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.qinghuatokendemo.Domain.Menu; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface MenuMapper extends BaseMapper<Menu> List<String> selectPermsByUserId(Long userid);
尤其是自定义方法,所以需要创建对应的mapper文件,定义对应的sql语句
<?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.example.qinghuatokendemo.Mapper.MenuMapper"> <select id="selectPermsByUserId" resultType="java.lang.String"> SELECT DISTINCT m.`perms` FROM sys_user_role ur LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id` LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id` LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id` WHERE user_id = #userid AND r.`status` = 0 AND m.`status` = 0 </select> </mapper>
在application.yml中配置mapperXML文件的位置
spring: datasource: url: jdbc:mysql://localhost:3306/springsecurity?characterEncoding=utf-8&serverTimezone=UTC username: root password: njzyb555 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml
测试类
package com.example.qinghuatokendemo; import com.example.qinghuatokendemo.Domain.User; import com.example.qinghuatokendemo.Mapper.MenuMapper; import com.example.qinghuatokendemo.Mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.List; @SpringBootTest public class MapperTest @Autowired private UserMapper userMapper; @Autowired private MenuMapper menuMapper; @Autowired private PasswordEncoder passwordEncoder; @Test public void TestBCryptPasswordEncoder() //$2a$10$9CmQULPcw8prFL.gnmM/zO1bDtPtVNb4mTxNs2wHsm7xonGMCvT2C System.out.println(passwordEncoder. matches("1234", "$2a$10$eAQvguaa3mHMt7cUrXeQnu3vIw74tbNtthm/t1gH6IMrRihv1OpRu")); /*String encode = passwordEncoder.encode("1234"); System.out.println(encode);*/ @Test public void testUser() List<User> users = userMapper.selectList(null); System.out.println(users); @Test public void testselectPermsByUserId() List<String> list = menuMapper.selectPermsByUserId(1L); System.out.println(list);
自定义失败处理
我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。
在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。
如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置给SpringSecurity即可。
自定义实现类
package com.example.qinghuatokendemo.Handler; import com.alibaba.fastjson.JSON; import com.example.qinghuatokendemo.Domain.ResponseResult; import com.example.qinghuatokendemo.Utils.WebUtils; import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AccessDeniedHandlerImpl implements AuthenticationEntryPoint @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException ResponseResult result = new ResponseResult(HttpStatus.INTERNAL_SERVER_ERROR.value(),"用户认证失败请查询登录"); String json = JSON.toJSONString(request); //处理异常 WebUtils.renderString(response,json);
package com.example.qinghuatokendemo.Handler; import com.alibaba.fastjson.JSON; import com.example.qinghuatokendemo.Domain.ResponseResult; import com.example.qinghuatokendemo.Utils.WebUtils; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class AccessDeniedHandlerImpl implements AccessDeniedHandler @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足"); String json = JSON.toJSONString(result); WebUtils.renderString(response,json);
package com.example.qinghuatokendemo.Config; import com.example.qinghuatokendemo.Handler.AccessDeniedHandlerImpl; import com.example.qinghuatokendemo.filter.JwtAuthenticationTokenFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter @Bean public PasswordEncoder passwordEncoder() return new BCryptPasswordEncoder(); @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private AuthenticationEntryPoint authenticationEntryPoint; @Autowired private AccessDeniedHandler accessDeniedHandler; @Override protected void configure(HttpSecurity http) throws Exception /* http //关闭csrf .csrf().disable() //不通过Session获取SecurityContext .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 对于登录接口 允许匿名访问 .antMatchers("/user/login").anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated();*/ http .csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/hello").permitAll() .antMatchers("/user/login").anonymous() .anyRequest().authenticated(); //把token校验过滤器添加到过滤器链中 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); //配置异常处理器 http.exceptionHandling() //认证失败处理器 .authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler); @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception return super.authenticationManagerBean();
Django(63)drf权限源码分析与自定义权限
前言
上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf
默认是允许所有用户访问
权限源码分析
源码入口:APIView.py
文件下的initial
方法下的check_permissions
def check_permissions(self, request):
"""
检查是否应允许该请求。如果请求不被允许,则引发适当的异常。
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, \'message\', None),
code=getattr(permission, \'code\', None)
)
权限在get_permissions
方法中获取到,源码如下:
def get_permissions(self):
"""
实例化并返回此视图所需的权限列表。
"""
return [permission() for permission in self.permission_classes]
permission_classes
又等于api_settings.DEFAULT_PERMISSION_CLASSES
,所以我们去settings.py
文件中查找
\'DEFAULT_PERMISSION_CLASSES\': [
\'rest_framework.permissions.AllowAny\',
],
我们会发现drf
默认的权限是AllowAny
,我们去看下源码:
class AllowAny(BasePermission):
"""
允许任意访问。这不是严格要求的,因为您可以使用空的 permission_classes 列表,但它很有用,因为它使意图更加明确。
"""
def has_permission(self, request, view):
return True
我们可以看到AllowAny
继承自BasePermission
,然后定义了has_permission
方法,返回值为True
。
drf为我们提供了4个系统权限认证:
1. AllowAny
认证规则全部返回True:`return True`
游客与登录用户都有所有权限
2. IsAuthenticated
认证规则必须有登录的合法用户:`return bool(request.user and request.user.is_authenticated)`
游客没有任何权限,登录用户才有权限
3. IsAdminUser
认证规则必须是:`return bool(request.user and request.user.is_staff)`
游客没有任何权限,登录用户才有权限
4. IsAuthenticatedOrReadOnly
认证规则必须是只读请求或者是合法用户无限制
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
游客只读,合法用户无限制
自定义认证类
- 创建继承
BasePermission
的权限类 - 实现
has_permission
方法 - 实现体根据权限规则 确定 有无权限
- 进行全局或局部配置(一般采用局部配置)
权限规则
满足设置的用户条件,代表有权限,返回True
不满足设置的用户条件,代表有权限,返回False
自定义权限
from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission
class MyPermissions(BasePermission):
def has_permission(self, request, view):
rule1 = request.method in [\'GET\', \'OPTIONS\', \'HEAD\']
group = Group.objects.filter(name="管理员").first()
groups = request.user.groups.all()
rule2 = group in groups
rule3 = group and groups
return rule1 or (rule2 and rule3)
以上定义了3条规则
- rule1:请求方法是
GET
、OPTIONS
、HEAD
游客和用户都可以访问 - rule2:当前用户如果有多个分组,其中必须有一个分组是管理员
- rule3:管理员分组必须存在,用户必须在分组中
接下里我们定义视图
class TestView(APIView):
permission_classes = [MyPermissions]
def get(self, request, *args, **kwargs):
print(request.user)
return APIResponse(data_msg="所有用户都可以访问")
def post(self, request, *args, **kwargs):
print(request.user)
return APIResponse(data_msg="只有管理员用户可以访问")
视图中只是添加了permission_classes = [MyPermissions]
属与局部配置,也就是自定义的权限只针对此视图,其他视图还是默认的全局配置,如果我们还有其他的关于权限的需求,只需要在自定义的权限类中写逻辑即可
以上是关于从数据库查询权限信息与自定义失败处理的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Data Studio 数据源列表中轻松找到直接 BigQuery 表数据源与自定义查询数据源?
ASP.NET Core 身份中的角色声明与自定义身份验证中的角色权限相比