SpringSecurity+Token实现权限校验
1.Spring Security简介
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
1.1.需求
实现权限校验,指定用户可以访问指定页面
1.2代码解读分析
1).继承WebSecurityConfigurerAdapter
类
2).重写 configure
方法
protected void configure(HttpSecurity httpSecurity)
注:此方法用来添加校验规则
/** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 认证失败处理类 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() // 对于登录login 验证码captchaImage 允许匿名访问 .antMatchers("/login", "/captchaImage").anonymous() .antMatchers( HttpMethod.GET, "/*.ttf", "/*.woff", "/*.gif", "/*.eot", "/*.json", "/*.woff2", "/*.png", "/*.ico", "/*.svg", "/*.jpg", "/*.html", "/**/*.ttf", "/**/*.woff", "/**/*.gif", "/**/*.eot", "/**/*.json", "/**/*.woff2", "/**/*.png", "/**/*.ico", "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js" ).permitAll(). antMatchers( //mybatis复习相关的接口全部放行,同学们可以通过postMan进行测试而不需要进行权限认证 "/review/**", "/review" ).permitAll() .antMatchers("/common/downloadByMinio**").permitAll() .antMatchers("/profile/**").anonymous() .antMatchers("/common/download**").anonymous() .antMatchers("/common/download/resource**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() .antMatchers("/druid/**").anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); }
上面有个jwt的过滤器
里面继承了一个类
如果请求中有token则放行
2).添加权限
那么 它是怎么添加权限的那?我们点进参数里看看...
可以看到里面是个接口,返回对象是个
咦。。。这个是不是放用户名和密码 和权限的那,那么看看它的实现类是怎么写的吧
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username); if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new BaseException("对不起,您的账号:" + username + " 已被删除"); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new BaseException("对不起,您的账号:" + username + " 已停用"); } return createLoginUser(user); }
可以看到首先根据用户名去查对象,并做了一系列判断,最后通过之后调用了这个方法
这个完成对象的初始化,那么这个调用的service又是干什么的那?
这个是把该用户所对应的全部权限,从数据库中查询出来存入集合中,完成对象初始化的
我们再回到之前的配置类
至此,该用户的所拥有的全部权限就获取到了,完成了每个用户所看到的界面不一样的情况,但是这个还要结合一个注解来使用
@PreAuthorize("@ss.hasPermi('clues:course:list')")
这个注解为 spring security 的原生注解 里面返回布尔类型数据,为true就走这个方法
点进去有这么个方法,是不是很熟悉,集合参数里放的就是刚才该用户的权限列表集合,如果包含传进来的字符串,则返回true
让其访问,至此功能就实现了
2.SpringBoot中JWT是如何使用的
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。中间用点(.)分隔成三个部分。注意JWT 内部是没有换行的。
2.1.优点
基于token的验证方式它有什么优点吗?
支持跨域访问,Cookie是不允许跨域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.
无状态:Token机制在服务端不需要存储session信息,因为Token自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.
解耦 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
适用性更广:只要是支持http协议的客户端,就可以使用token认证。
服务端只需要验证token的安全,不必再去获取登录用户信息,因为用户的登录信息已经在token信息中。
基于标准化:你的API可以采用标准化的 JSON Web Token (JWT).
2.1.1.组成
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。中间用点(.)分隔成三个部分。注意JWT 内部是没有换行的。
2.2.使用
1).当前端传过来登录请求数据时,把对象生成Token存起来
public String createToken(LoginUser loginUser) { String token = IdUtils.fastUUID(); loginUser.setToken(token); setUserAgent(loginUser); refreshToken(loginUser); Map<String, Object> claims = new HashMap<>(); claims.put(Constants.LOGIN_USER_KEY, token); //存放非敏感信息 claims.put("username",loginUser.getUsername()); claims.put("nickName",loginUser.getUser().getNickName()); claims.put("createTime",loginUser.getUser().getCreateTime()); return createToken(claims); }
这里用了两个方法
第一个:
/** * 设置用户代理信息 * * @param loginUser 登录信息 */ public void setUserAgent(LoginUser loginUser) { UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); String ip = IpUtils.getIpAddr(ServletUtils.getRequest()); loginUser.setIpaddr(ip); loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); loginUser.setBrowser(userAgent.getBrowser().getName()); loginUser.setOs(userAgent.getOperatingSystem().getName()); }
设置代理信息
第二个:
/** * 刷新令牌有效期 * * @param loginUser 登录信息 */ public void refreshToken(LoginUser loginUser) { loginUser.setLoginTime(System.currentTimeMillis()); loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); // 根据uuid将loginUser缓存 String userKey = getTokenKey(loginUser.getToken()); redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); }
刷新令牌有效期,并将对象存入Redis中
最后调用了这个方法,返回生成的令牌
/** * 从数据声明生成令牌 * * @param claims 数据声明 * @return 令牌 */ private String createToken(Map<String, Object> claims) { String token = Jwts.builder() .setClaims(claims) .signWith(SignatureAlgorithm.HS512, secret).compact(); return token; }
secret
为签名,保证数据安全性
2).接下来我们看看如何取出来
可以看到我们传进去的是一个HttpServletRequest
请求,因为我们把Token放请求头里了,那么我们点进去看看,是怎么取的
这个方法总共调用了这三个方法,最终取出来了user对象,接下来一步一步看吧
第一个方法获取Token字符串
第二个方法根据Token字符串获取集合
第三个方法根据Token存入的唯一的UUID获得存入缓存中的key
最终取到user对象,至此Token存取完成。
以上为个人梳理,有不对的地方还请指正
本文作者:七友的二度浪漫
本文链接:https://www.cnblogs.com/sy2022/p/16592767.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步