若依登入权限理解

324 阅读3分钟

请求

当请求一个/system/user/deptTree 部门树的接口请求

后台经历了哪些过程

首先

1.会经过一个过滤器,在JwtAuthenticationTokenFilter类中的doFilterInternal方法拿到request,通过request.getHeader("Authorization")方法拿到整个token。

2.使用Jwts.parser()...解析token中的数据,获得登录时存在JWT中的数据,这个数据是一个uuid,而这个uuid对应着登录时保存在redis中用户信息的key,通过这个key就拿到了这个用户的所有信息。然后去更新redis中这个key的过期时间,这个过期时间就是配置文件中令牌的有效期。

//解析token中的数据
private Claims parseToken(String token)
    {

        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }
//Claims作用
User user = xxxx;//你的用户实体类
Map<String, Object> test = new HashMap<>();
test.put("id", user.getId());
test.put("name", user.getName());
String token = JWTUtil.createJWT(test);
return R.ok().put("token", token);


public R test(HttpServletRequest request) {
        String token = request.getHeader(AuthConsts.AUTHORIZATION_HEADER);
        Claims claims = JWTUtil.parseJWT(appConfig.getJwtSecret(), token);
        System.out.println("这是id"+claims.get("id", Integer.class));
        System.out.println("这是名称"+claims.get("name", String.class));
        return R.data(claims);
    }
输出>这是id 1
输出> 这是名称 DadiaoMan

3.设置UsernamePasswordAuthenticationToken authenticationToken的值,分别为:

principal为当前用户loginUser

credentials为null

authorities为loginUser.getAuthorities()

Details为当前访问主机的ip地址

设置安全上下文的Authentication,SecurityContextHolder.getContext().setAuthentication(authenticationToken)

过滤器的工作完成了

然后

4.如果当前的接口加了@PreAuthorize("@ss.hasPermi('system:dept:list')")注解,就会进入PermissionService这个Service,PermissionService中有一个 hasPermi(String permission)方法,里面通过LoginUser loginUser =SecurityUtils.getLoginUser();获取用户信息,用来查询当前用户是否有'system:dept:list'的权限字符。如果有这个权限信息,设置PermissionContextHolder(权限信息)的值为'system:dept:list'(可以这么理解,实际上是将这个值给了RequestContextHolder),然后进入这个接口内部执行具体方法。如果有这个权限信息,报403,"没有权限,请联系管理员授权"

//SecurityUtils类中的方法
public static LoginUser getLoginUser()
    {
        try
        {
            Object principal = getAuthentication().getPrincipal();
            return (LoginUser) getAuthentication().getPrincipal();

        }
        catch (Exception e)
        {
            throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
        }
    }
public static Authentication getAuthentication()
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();//第3步中设置的值
        return SecurityContextHolder.getContext().getAuthentication();
    }

而在SecurityUtils类中获取的权限信息就是在第3步中设置的值,SecurityContextHolder.getContext().getAuthentication();

补充:RequestContextHolder的作用(SpringMVC之RequestContextHolder分析 - 水狼一族 - 博客园 (cnblogs.com)

​ 正常来说在service层是没有request的,然而直接从controlller传过来的话解决方法太粗暴,后来发现了SpringMVC提供的RequestContextHolder,可以获取到request

最后

5.进入service层的方法,service加了@DataScope(deptAlias = "d")注解

在DataScopeAspect类中有拦截加了@DataScope注解的方法

@Before("@annotation(controllerDataScope)") //拦截加了@DataScope
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
{
    clearDataScope(point);
    handleDataScope(point, controllerDataScope);
}

在DataScopeAspect里面获取部门信息,设置补充的sql。

设置${params.dataScope}

BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");

parameterType="SysDept" SysDept继承了BaseEntity,所以${params.dataScope}会生效

public class SysDept extends BaseEntity
<select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult">
       <include refid="selectDeptVo"/>
       where d.del_flag = '0'
   <if test="deptId != null and deptId != 0">
      AND dept_id = #{deptId}
   </if>
       <if test="parentId != null and parentId != 0">
      AND parent_id = #{parentId}
   </if>
   <if test="deptName != null and deptName != ''">
      AND dept_name like concat('%', #{deptName}, '%')
   </if>
   <if test="status != null and status != ''">
      AND status = #{status}
   </if>
   <!-- 数据范围过滤 -->
   ${params.dataScope}
   order by d.parent_id, d.order_num
   </select>

只能在controller跳到service层@DataScope生效问题。。。

@Aspect如果被spring容器加载的话,而@Controller注解的这些类的实例化以及注入却是由SpringMVC来完成。 @Aspect如果被spring容器加载的时候,可能Spring MVC容器还未初始化, Controller类还未初始化,所以无法正常织入

想在service内部调用的话使用

SpringUtils.getAopProxy(this).selectDeptList(dept);

登录

首先通过/captchaImage接口获取验证码

然后调用/login接口来验证

在/login这个接口中调用SysLoginService的方法

1.验证码校验,验证码信息是存在redis中的验证码信息

2.前置校验,校验用户名和密码是否符合格式、ip黑名单等等

3.将用户信息存进UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);