项目集成Spring Security(一)

在上一篇基础上继续集成 JWT ,实现用户身份验证。

前言

前后端分离项目中,如果直接把 API 接口对外开放,我们知道这样风险是很大的,所以在上一篇中我们引入了 Spring Security ,但是我们在登陆后缺少了请求凭证部分。

什么是JWT?

JWT是 Json Web Token 的缩写。它是基于 RFC 7519 标准定义的一种可以安全传输的 小巧 和 自包含 的JSON对象。由于数据是使用数字签名的,所以是可信任的和安全的。JWT可以使用HMAC算法对secret进行加密或者使用RSA的公钥私钥对来进行签名。

JWT的工作流程

1、用户进入登录页,输入用户名、密码,进行登录;
2、服务器验证登录鉴权,如果改用户合法,根据用户的信息和服务器的规则生成 JWT Token
3、服务器将该 token 以 json 形式返回(不一定要json形式,这里说的是一种常见的做法)
4、用户得到 token,存在 localStorage、cookie 或其它数据存储形式中。以后用户请求 /protected 中的 API 时,在请求的 header 中加入 Authorization: Bearer xxxx(token)。此处注意token之前有一个7字符长度的 Bearer。
5、服务器端对此 token 进行检验,如果合法就解析其中内容,根据其拥有的权限和自己的业务逻辑给出对应的响应结果。
6、用户取得结果

如下如所示:

来看一下 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

token 分成了三部分,头部(header),荷载(Payload) 和 签名(Signature),每部分用 . 分隔,其中头部和荷载使用了base64编码,分别解码之后得到两个JSON串:

第一部分-头部:

{"alg": "HS256","typ": "JWT"
}

alg字段为加密算法,这是告诉我们 HMAC 采用 HS512 算法对 JWT 进行的签名。

第二部分-荷载:

{"sub": "1234567890","name": "John Doe","iat": 1516239022
}

荷载的字段及含义:

  • iss: 该JWT的签发者
  • sub: 该JWT所面向的用户
  • aud: 接收该JWT的一方
  • exp(expires): 什么时候过期,这里是一个Unix时间戳
  • iat(issued at): 在什么时候签发的

这段告诉我们这个Token中含有的数据声明(Claim),这个例子里面有三个声明:sub, name 和 iat。在我们这个例子中,分别代表着
所面向的用户、用户名、创建时间,当然你可以把任意数据声明在这里。

第三部分-签名:

第三部分签名则不能使用base64解码出来,该部分用于验证头部和荷载数据的完整性。

JWT的生成和解析

引入依赖:

<!-- JWT -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

创建一个测试类尝试一下 JWT 的生成:

public class Test {public static void main(String[] args){String token = Jwts.builder()主题 放入用户名.setSubject("niceyoo")自定义属性 放入用户拥有请求权限.claim("authorities","admin")失效时间.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))签名算法和密钥.signWith(SignatureAlgorithm.HS512, "tmax").compact();System.out.println(token);}}

控制台打印如下:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1ODM1M30.keCiHrcEr0IWXfZLocgHS8znn7uSiaZW1IT6bTs-EQG0NPsb6-Aw_XbGQea4mez2CcAflgMqtzIpsDjZsUOVug

数据声明(Claim)是一个自定义属性,可以用来放入用户拥有请求权限。上边为简单直接传了一个 ‘admin’。

再看看解析:

public static void main(String[] args){try {解析tokenClaims claims = Jwts.parser().setSigningKey("tmax").parseClaimsJws("eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJuaWNleW9vIiwiYXV0aG9yaXRpZXMiOiJhZG1pbiIsImV4cCI6MTU1OTQ1OTc2Mn0.MkSJtGaVePLa-eM3gylh1T3fwODg-6ceDDOxscXAQKun-qNrbQFcKPNqXhblbXPNLhaJyEnwugNANCTs98UNmA").getBody();System.out.println(claims);获取用户名String username = claims.getSubject();System.out.println("username:"+username);获取权限String authority = claims.get("authorities").toString();System.out.println("权限:"+authority);} catch (ExpiredJwtException e) {System.out.println("jwt异常");} catch (Exception e){System.out.println("异常");}
}

控制台打印:

{sub=niceyoo, authorities=admin, exp=1559459762}
username:niceyoo
权限:admin

JWT 本身没啥难度,但安全整体是一个比较复杂的事情,JWT 只不过提供了一种基于 token 的请求验证机制。但我们的用户权限,对于 API 的权限划分、资源的权限划分,用户的验证等等都不是JWT负责的。也就是说,请求验证后,你是否有权限看对应的内容是由你的用户角色决定的。所接下来才是我们的重点,Spring Security 整合 JWT。

集成JWT

要想要 JW T在 Spring 中工作,我们应该新建一个 JWT filter,并把它配置在 WebSecurityConfig 中。

WebSecurityConfigurerAdapter.java

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate AuthenticationSuccessHandler successHandler;@Autowiredprivate AuthenticationFailHandler failHandler;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());加密}@Overrideprotected void configure(HttpSecurity http) throws Exception {ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();registry.and()表单登录方式.formLogin().permitAll()成功处理类.successHandler(successHandler)失败.failureHandler(failHandler).and().logout().permitAll().and().authorizeRequests()任何请求.anyRequest()需要身份认证.authenticated().and()关闭跨站请求防护.csrf().disable()前后端分离采用JWT 不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()添加JWT过滤器 除已配置的其它请求都需经过此过滤器.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));}
}

相较于上一篇主要多了如下一行配置:

.addFilter(new JWTAuthenticationFilter(authenticationManager(), 7));

JWTAuthenticationFilter.java

@Slf4j
public class JWTAuthenticationFilter extends BasicAuthenticationFilter   {private Integer tokenExpireTime;public JWTAuthenticationFilter(AuthenticationManager authenticationManager, Integer tokenExpireTime) {super(authenticationManager);this.tokenExpireTime = tokenExpireTime;}public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {super(authenticationManager, authenticationEntryPoint);}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String header = request.getHeader(SecurityConstant.HEADER);if(StrUtil.isBlank(header)){header = request.getParameter(SecurityConstant.HEADER);}Boolean notValid = StrUtil.isBlank(header) || (!header.startsWith(SecurityConstant.TOKEN_SPLIT));if (notValid) {chain.doFilter(request, response);return;}try {UsernamePasswordAuthenticationToken 继承 AbstractAuthenticationToken 实现 Authentication所以当在页面中输入用户名和密码之后首先会进入到 UsernamePasswordAuthenticationToken验证(Authentication),UsernamePasswordAuthenticationToken authentication = getAuthentication(header, response);SecurityContextHolder.getContext().setAuthentication(authentication);}catch (Exception e){e.toString();}chain.doFilter(request, response);}private UsernamePasswordAuthenticationToken getAuthentication(String header, HttpServletResponse response) {用户名String username = null;权限List<GrantedAuthority> authorities = new ArrayList<>();try {解析tokenClaims claims = Jwts.parser().setSigningKey(SecurityConstant.JWT_SIGN_KEY).parseClaimsJws(header.replace(SecurityConstant.TOKEN_SPLIT, "")).getBody();logger.info("claims:"+claims);获取用户名username = claims.getSubject();logger.info("username:"+username);获取权限String authority = claims.get(SecurityConstant.AUTHORITIES).toString();logger.info("authority:"+authority);if(!StringUtils.isEmpty(authority)){authorities.add(new SimpleGrantedAuthority(authority));}} catch (ExpiredJwtException e) {ResponseUtil.out(response, ResponseUtil.resultMap(false,401,"登录已失效,请重新登录"));} catch (Exception e){log.error(e.toString());ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"解析token错误"));}if(StrUtil.isNotBlank(username)) {踩坑提醒 此处password不能为nullUser principal = new User(username, "", authorities);return new UsernamePasswordAuthenticationToken(principal, null, authorities);}return null;}
}

接下来我们启动项目看看:

访问项目中已有的链接:

http://localhost:7777/tmax/videoCategory/getAll

老样子认证一波:

其中 niceyoo、****** 为数据库用户信息

登陆成功后获取返回的 token,注意,此 token 是由 JWT 生成的:

 String token = SecurityConstant.TOKEN_SPLIT + Jwts.builder()主题 放入用户名.setSubject(username)自定义属性 放入用户拥有请求权限.claim(SecurityConstant.AUTHORITIES, authorities)失效时间.setExpiration(new Date(System.currentTimeMillis() + 7 * 60 * 1000))签名算法和密钥.signWith(SignatureAlgorithm.HS512, SecurityConstant.JWT_SIGN_KEY).compact();

浏览器返回 token 如下:

然后我们通过 token 凭证去访问上边的方法:

后台打印信息:

claims:{sub=niceyoo, authorities=admin, exp=1559472866}
username:niceyoo
authority:admin

随便改一下 token ,返回如下:

SpringSecurity 整合 JWT相关推荐

  1. SpringSecurity权限管理系统实战—六、SpringSecurity整合JWT

    文章目录 系列目录 前言 一.无状态登录 二.JWT介绍 1.什么是jwt 头部(Header) 载荷(Payload) 签名(Signature) 2.JWT工作流程 3.简单实现 三.整合JWT ...

  2. SpringSecurity - 整合JWT使用 Token 认证授权

    一.SpringSecurity 前面讲解了SpringSecurity的动态认证和动态权限角色,我们都知道在现在大多都是微服务前后端分离的模式开发,前面讲的还是基于Session的,本篇我们整合JW ...

  3. springsecurity整合jwt实现授权认证,权限分配

    1.前期准备工作 1.1首先需要导入jwt依赖和springsecurity的依赖 <!--security 依赖 --><dependency><groupId> ...

  4. SpringSecurity整合JWT

    文章目录 1 无状态登录 1.1 什么是有状态? 1.2 什么是无状态 1.3 如何实现无状态 1.4 JWT 1.5 JWT 存在的问题 2 实战 2.1 环境搭建 2.2 JWT 过滤器配置 2. ...

  5. JWT教程_2 SpringSecurity与JWT整合

    3. SpringSecurity与JWT整合 依赖: <dependencies><dependency><groupId>org.mybatis.spring. ...

  6. GitHub开源项目学习 电商系统Mall (五) mall整合SpringSecurity和JWT实现认证和授权(二)

    mall整合SpringSecurity和JWT实现认证和授权(二) https://github.com/macrozheng/mall 登录注册功能实现 UmsAdminController类 实 ...

  7. GitHub开源项目学习 电商系统Mall (四) mall整合SpringSecurity和JWT实现认证和授权(一)

    mall整合SpringSecurity和JWT实现认证和授权(一) https://github.com/macrozheng/mall 跳过了官方Learning中较简单的Swagger-UI的实 ...

  8. 商城项目(三)整合SpringSecurity和JWT实现认证和授权

    整合SpringSecurity和JWT实现认证和授权 环境搭建 SpringSecurity JWT Hutool 项目使用表说明 ums_admin:后台用户表 ums_role:后台用户角色表 ...

  9. Spring Security整合JWT,实现单点登录,So Easy~!

    前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况 ...

最新文章

  1. [转]实战 SQL Server 2008 数据库误删除数据的恢复
  2. python学习笔记3-循环1
  3. IIS连接数、IIS并发连接数、IIS最大并发工作线程数、应用程序池的队列长度、应用程序池的...
  4. (转)学习密度与专注力
  5. python快递费用计算_[Python]简单用Python写个查询快递的程序最后附源代码
  6. hbuilder php xdebug,Hbuilder使用xdebug配置php断点调试
  7. php解析url的三种方法举例
  8. BaiduMapsApiDemo报错:请在 DemoApplication.java文件输入正确的授权Key
  9. mysql join using temporary 优化_MySQL 在 INNER JOIN 场景的使用-爱可生
  10. ubuntu 搜狗输入法的安装
  11. 给Ubuntu安装MacOS主题
  12. python实训第一天
  13. InstantiationException in hadoop map reduce program
  14. 通用商品后端管理系统
  15. 如何修改iTunes的iPhone备份路径?
  16. html中的空格怎么输入法,输入法空格怎么打
  17. Spring Boot入门系列(十八)整合mybatis,使用注解的方式实现增删改查
  18. TM1650数码管驱动芯片
  19. Go语言学习之打印九九乘法表
  20. 项目经理(Project Manager)和产品经理(Product Manager),简称PM。

热门文章

  1. 前端学习(2887):如何短时间内实现v-for proxy代理
  2. 前端学习(2871):Vue路由权限『前后端全解析』2
  3. [js] js循环中调用异步的方法,如何确保执行结果的顺序是正确的?
  4. 前端学习(2338):记录解决问题的一次
  5. 前端学习(2330):angular之二级路由
  6. 前端学习(1804):前端调试之列表伪类
  7. 前端学习(531):什么是等高布局
  8. mybatis学习(55):延迟加载
  9. 项目管理(3):备战pmp
  10. java学习(98):线程join使用中断进行另一个