范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

干货SpringBootSpringSecurityJwt权限认证认证

  1. 整体逻辑1. SpringSecurity认证的逻辑规则
  启动项目时,SpringBoot自动检索所有带@Configuration的注解,所以就将我们的WebSecurityConfig给加载了,这个config中,我们需要在configure(AuthenticationManagerBuilder auth)方法中注册一个继承自UserDetailsService的接口,这个接口中只有一个方法,那就是使用username获取到数据库中用户信息并返回成UserDetail实体。这个方法需要我们按照我们的不同业务场景重写
  WebSecurityConfig /**  * @description:  * @author: coderymy  * @create: 2020-10-01 13:54  * 

* 1. 创建WebSecurityConfig 类继承WebSecurityConfigurerAdapter * 2. 类上加上@EnableWebSecurity,注解中包括@Configuration注解 *

* WebSecurityConfigurerAdapter声明了一些默认的安全特性 * (1)验证所有的请求 * (2)可以使用springSecurity默认的表单页面进行验证登录 * (3)允许用户使用http请求进行验证 */ /** * 如何自定义认证 * 1. 实现并重写configure(HttpSecurity http)方法,鉴权,也就是判断该用户是否有访问该api的权限 *

*

* 页面显示403错误,表示该用户授权失败(401代表该用户认证失败)前端可以使用返回的状态码来标识如何给用户展示 * 用2XX表示本次操作成功,用4XX表示是客户端导致的失败,用5XX表示是服务器引起的错误 */ @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public static void main(String[] args) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encode = passwordEncoder.encode("123"); System.out.println(encode); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; } /** * SpringSecurity5.X要求必须指定密码加密方式,否则会在请求认证的时候报错 * 同样的,如果指定了加密方式,就必须您的密码在数据库中存储的是加密后的,才能比对成功 * * @return */ @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //鉴权 @Override protected void configure(HttpSecurity http) throws Exception { /** * 1. HttpSecurity被声明为链式调用 * 其中配置方法包括 * 1. authorizeRequests()url拦截配置 * 2. formLogin()表单验证 * 3. httpBasic()表单验证 * 4. csrf()提供的跨站请求伪造防护功能 */ /** * 2. authorizeRequests目的是指定url进行拦截的,也就是默认这个url是"/"也就是所有的 * anyanyRequest()、antMatchers()和regexMatchers()三种方法来拼配系统的url。并指定安全策略 */ http.authorizeRequests() //这里指定什么样的接口地址的请求,需要什么样的权限 ANT模式的URL匹配器 .antMatchers("/select/**").hasRole("USER")//用户可以有查询权限 .antMatchers("/insert/**").hasRole("ADMIN")//管理员可以有插入权限权限 .antMatchers("/empower/**").hasRole("SUPERADMIN")//超级管理员才有赋权的权限 .antMatchers("/login/**").permitAll()//标识list所有权限都可以直接访问,即使不登录也可以访问。一般将login页面放给这个权限 .and() .formLogin() // .loginProcessingUrl("/login/user")//用来定义什么样的API请求时login请求 // .permitAll()//login请求需要是所有权限都可以的 .and().csrf().disable(); /** * 将自定义的JWT过滤器加入configure中 */ JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(this.authenticationManager()); http.addFilterBefore(jwtAuthenticationFilter, JWTAuthenticationFilter.class); } @Autowired private MyUserDetailsService myUserDetailsService; //认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService); } }   MyUserDetailsService @Service public class MyUserDetailsService implements UserDetailsService { @Resource private UsersRepository usersRepository; /** * 其实这样就完成了认证的过程,能获取到数据库中配置的用户信息 * * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //获取该用户的信息 Users user = usersRepository.findByUsername(username); if (user == null) {//用户不存在报错 throw new UsernameNotFoundException("用户不存在"); } /** * 将roles信息转换成SpringSecurity内部的形式,即Authorities * commaSeparatedStringToAuthorityList可以将使用,隔开的角色列表切割出来并赋值List * 如果不行的话,也可以自己实现这个方法,只要拆分出来就可以了 */ //注意,这里放入Authorities中的信息,都需要是以Role_开头的,所以我们在数据库中配置的都是这种格式的。当我们使用hasRole做比对的时候,必须要是带Role_开头的。否则可以使用hasAuthority方法做比对 user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles())); return user; } }   其实如果去掉上面的将自定义的JWT过滤器加入到过滤链中的话,这个认证过程已经完成了。使用下面的代码就可以调用起整个认证程序。   核心代码authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword()));   这一行就会将username和password放到认证程序中进行认证。   也就是需要我们自己的逻辑让他去触发这个代码的实现。就可以自动完成认证程序了。就会触发使用username获取到数据库用户信息,然后经过密码加密比对之后会将认证结果返回。   我们整合JWT其实也很简单,其实就是将JWT的登录部分的操作,使用过滤器封装,将该过滤器放到整个认证的过滤链中2. 自定义JWT过滤器的配置   SpringSecurity过滤器的配置无非以下几个条件该过滤器过滤什么样的API请求(也就是说什么样的API请求会触发该过滤器执行)。配置被过滤的请求API该过滤器做什么事该过滤器执行成功以及执行失败的各种情况该怎么做该过滤器执行的时机是什么样的,也就是在过滤链之前还是之后执行   先解决逻辑上以上三个问题的答案我们需要拦截认证请求,肯定是形如xxx/login/xxx这种API接口的请求啦这个过滤器会做什么事呢?首先,我们需要进行用户名密码的基础验证,也就是合不合法我们需要调用起SpringSecurity的默认认证程序认证程序执行成功之后,我们需要按照用户的信息以JWT的规则生成一个JWTToken并将其放入response中返回回去认证程序执行失败,也就是用户登录失败,我们也需要将返回信息封装起来返回给用户执行成功,我们需要返回给用户JWTToken信息。执行失败,我们也要友好提示用户如果排除其他的业务场景干扰,目前过滤链只有进行鉴权时候才使用。所以针对不同的业务场景,这个过滤器放的地方其实是不一样的。(之后我们的另一个JWTToken校验的过滤器应该需要在这个认证的过滤器之后(两个其实并不捕捉同样的APi所以不会依次执行。也不用太考虑这个问题))(我记得SpringCloud中的zuul网关的过滤器是可以自定义级别的。但是目前在SpringSecurity中尚未发现这种功能)   针对以上解答,下面用代码来做展示(ps:序号依次对应上面)配置过滤器过滤地址 /** * 下面是为了配置这个Manager * 配置其拦截的API请求地址 * * @param authenticationManager */ public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; super.setFilterProcessesUrl("/login/user");//这里指定什么样的API请求会被这个过滤器拦截 } 配置过滤器职能 @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //这里是定义一个拦截器,在认证方面的拦截器,当请求的时候回拦截到这里面然后进行身份认证 //验证用户名密码是否正确之后 Authentication authenticate = null; try { System.out.println("InputStream:" + request.getInputStream()); UserDto userDto = new ObjectMapper().readValue(request.getInputStream(), UserDto.class);//这个地方相当于封装一下请求,因为前台请求的是user.username="xxx"这种对象的形式。 //对于这个过滤器拦截的接口,去调用SpringSecurity默认的认证程序,也就是去进行SpringSecurity的认证 authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword())); return authenticate; //如果返回成功,就进入successfulAuthentication。返回失败就进入unsuccessfulAuthentication //可以通过下面的定义来让前端得到不同的返回从而向用户展示不同的效果 //TODO 这个地方还有点问题,如果密码错误了,就会报BadCredentialsException错误。需要看一下如果不让这么报错并让他进入到unsuccessfulAuthentication方法中 } catch (BadCredentialsException e) {//捕捉密码验证错误异常 log.info("密码错误"); try { this.unsuccessfulAuthentication(request, response, e); } catch (IOException ex) { ex.printStackTrace(); } catch (ServletException ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { //当身份验证通过之后,会进入这里,这里可以定义成功的返回 Users user = (Users) authResult.getPrincipal();//将principal中的信息转换成User对象 JwtUtil util = new JwtUtil(secretKey, SignatureAlgorithm.HS256); Map map = new HashMap<>(); map.put("username", user.getUsername()); map.put("password", user.getPassword()); String jwtToken = util.encode("tom", 30000, map); response.addHeader("Authorizations", jwtToken); user.setJwtToken(jwtToken); ResponseUtil.write(response, JSONObject.toJSONString(ResultUtil.success(user))); System.out.println(response.getHeaderNames()); // super.successfulAuthentication(request, response, chain, authResult); //注意,不要使用默认的super来定义,否则上述会失效的 } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { //TODO 得看一下为啥会报这个错误 System.out.println("认证失败"); ResponseUtil.write(response, JSONObject.toJSONString(ResultUtil.error("登录失败,账号密码错误"))); } 执行失败与成功,分别是2中的unsuccessfulAuthentication和successfulAuthentication方法配置过滤链执行的位置在WebSecurityConfig中的configure(HttpSecurity http)方法中 JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(this.authenticationManager()); http.addFilterBefore(jwtAuthenticationFilter, JWTAuthenticationFilter.class); 复制代码   完成了以上的配置,前台就可以使用/login/user来进行登录操作了。登录成功会返回一个JSON对象来供前端判断成功与否2. 代码结果   全部代码奉上,随意写的注释有点多,不看的可以给删掉WebSecurityConfigimport org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @description: * @author: coderymy * @create: 2020-10-01 13:54 *

* 1. 创建WebSecurityConfig 类继承WebSecurityConfigurerAdapter * 2. 类上加上@EnableWebSecurity,注解中包括@Configuration注解 *

* WebSecurityConfigurerAdapter声明了一些默认的安全特性 * (1)验证所有的请求 * (2)可以使用springSecurity默认的表单页面进行验证登录 * (3)允许用户使用http请求进行验证 */ /** * 如何自定义认证 * 1. 实现并重写configure(HttpSecurity http)方法,鉴权,也就是判断该用户是否有访问该api的权限 *

*

* 页面显示403错误,表示该用户授权失败(401代表该用户认证失败)前端可以使用返回的状态码来标识如何给用户展示 * 用2XX表示本次操作成功,用4XX表示是客户端导致的失败,用5XX表示是服务器引起的错误 */ @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public static void main(String[] args) { BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encode = passwordEncoder.encode("123"); System.out.println(encode); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; } /** * SpringSecurity5.X要求必须指定密码加密方式,否则会在请求认证的时候报错 * 同样的,如果指定了加密方式,就必须您的密码在数据库中存储的是加密后的,才能比对成功 * * @return */ @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //鉴权 @Override protected void configure(HttpSecurity http) throws Exception { /** * 1. HttpSecurity被声明为链式调用 * 其中配置方法包括 * 1. authorizeRequests()url拦截配置 * 2. formLogin()表单验证 * 3. httpBasic()表单验证 * 4. csrf()提供的跨站请求伪造防护功能 */ /** * 2. authorizeRequests目的是指定url进行拦截的,也就是默认这个url是"/"也就是所有的 * anyanyRequest()、antMatchers()和regexMatchers()三种方法来拼配系统的url。并指定安全策略 */ http.authorizeRequests() //这里指定什么样的接口地址的请求,需要什么样的权限 ANT模式的URL匹配器 .antMatchers("/select/**").hasRole("USER")//用户可以有查询权限 .antMatchers("/insert/**").hasRole("ADMIN")//管理员可以有插入权限权限 .antMatchers("/empower/**").hasRole("SUPERADMIN")//超级管理员才有赋权的权限 .antMatchers("/login/**").permitAll()//标识list所有权限都可以直接访问,即使不登录也可以访问。一般将login页面放给这个权限 .and() .formLogin() // .loginProcessingUrl("/login/user")//用来定义什么样的API请求时login请求 // .permitAll()//login请求需要是所有权限都可以的 .and().csrf().disable(); /** * 将自定义过滤器加入configure中 */ JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(this.authenticationManager()); http.addFilterBefore(jwtAuthenticationFilter, JWTAuthenticationFilter.class); } @Autowired private MyUserDetailsService myUserDetailsService; //认证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService); } } JWTAuthenticationFilterimport com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; import com.huanong.avatarma.basic.entity.Users; import com.huanong.avatarma.basic.model.vo.UserDto; import com.huanong.avatarma.common.util.JwtUtil; import com.huanong.avatarma.common.util.ResponseUtil; import com.huanong.avatarma.common.util.ResultUtil; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.stereotype.Component; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @description: jwt拦截器 * 这个定义完成之后,想要生效还需要将其加入到过滤链中 * @author: coderymy * @create: 2020-10-02 09:36 **/ @Slf4j public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { /** * 验证用户名密码是否正确之后,生成一个token,并将token返回客户端 * * @param request * @param response * @return * @throws AuthenticationException */ private String secretKey = "ILoveDanChaoFan"; private AuthenticationManager authenticationManager; /** * 下面是为了配置这个Manager * 配置其拦截的API请求地址 * * @param authenticationManager */ public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; super.setFilterProcessesUrl("/login/user");//这里指定什么样的API请求会被这个过滤器拦截 } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //这里是定义一个拦截器,在认证方面的拦截器,当请求的时候回拦截到这里面然后进行身份认证 //验证用户名密码是否正确之后 Authentication authenticate = null; try { System.out.println("InputStream:" + request.getInputStream()); UserDto userDto = new ObjectMapper().readValue(request.getInputStream(), UserDto.class);//这个地方相当于封装一下请求,因为前台请求的是user.username="xxx"这种对象的形式。 //对于这个过滤器拦截的接口,去调用SpringSecurity默认的认证程序,也就是去进行SpringSecurity的认证 authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword())); return authenticate; //如果返回成功,就进入successfulAuthentication。返回失败就进入unsuccessfulAuthentication //可以通过下面的定义来让前端得到不同的返回从而向用户展示不同的效果 //TODO 这个地方还有点问题,如果密码错误了,就会报BadCredentialsException错误。需要看一下如果不让这么报错并让他进入到unsuccessfulAuthentication方法中 } catch (BadCredentialsException e) {//捕捉密码验证错误异常 log.info("密码错误"); try { this.unsuccessfulAuthentication(request, response, e); } catch (IOException ex) { ex.printStackTrace(); } catch (ServletException ex) { ex.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { //当身份验证通过之后,会进入这里,这里可以定义成功的返回 Users user = (Users) authResult.getPrincipal();//将principal中的信息转换成User对象 JwtUtil util = new JwtUtil(secretKey, SignatureAlgorithm.HS256); Map map = new HashMap<>(); map.put("username", user.getUsername()); map.put("password", user.getPassword()); String jwtToken = util.encode("tom", 30000, map); response.addHeader("Authorizations", jwtToken); user.setJwtToken(jwtToken); ResponseUtil.write(response, JSONObject.toJSONString(ResultUtil.success(user))); System.out.println(response.getHeaderNames()); // super.successfulAuthentication(request, response, chain, authResult); //注意,不要使用默认的super来定义,否则上述会失效的 } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { //TODO 得看一下为啥会报这个错误 System.out.println("认证失败"); ResponseUtil.write(response, JSONObject.toJSONString(ResultUtil.error("登录失败,账号密码错误"))); } } MyUserDetailsServiceimport com.huanong.avatarma.basic.dao.UsersRepository; import com.huanong.avatarma.basic.entity.Users; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @description: * @author: coderymy * @create: 2020-10-01 15:55 **/ @Service public class MyUserDetailsService implements UserDetailsService { @Resource private UsersRepository usersRepository; /** * 其实这样就完成了认证的过程,能获取到数据库中配置的用户信息 * * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //获取该用户的信息 Users user = usersRepository.findByUsername(username); if (user == null) {//用户不存在报错 throw new UsernameNotFoundException("用户不存在"); } /** * 将roles信息转换成SpringSecurity内部的形式,即Authorities * commaSeparatedStringToAuthorityList可以将使用,隔开的角色列表切割出来并赋值List * 如果不行的话,也可以自己实现这个方法,只要拆分出来就可以了 */ //注意,这里放入Authorities中的信息,都需要是以Role_开头的,所以我们在数据库中配置的都是这种格式的。当我们使用hasRole做比对的时候,必须要是带Role_开头的。否则可以使用hasAuthority方法做比对 user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles())); return user; } } Users@Data @ToString @Entity @Table(name = "users") public class Users implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private boolean enable; private String roles; private Date createDate; private Date modifyDate; @Transient//这个注解可以帮助在entity中添加表中没有的字段 private List authorities; @Transient private String jwtToken; @Override public Collection<? extends GrantedAuthority> getAuthorities() { //这个本身是对应roles字段的,但是因为结构不一致,所以重新创建一个,后续补充这部分 return this.authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } public void setAuthorities(List authorities) { this.authorities = authorities; } @Override public boolean isEnabled() { return this.enable; } } Resultimport lombok.Data; import lombok.ToString; /** * @description: * @author: coderymy * @create: 2020-10-02 13:24 **/ @Data @ToString public class Result { private Integer code; private String msg; private Object data; } 复制代码 JwtUtil/** * @description: * @author: coderymy * @create: 2020-10-02 09:24 **/ import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.apache.commons.codec.binary.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.*; /* * 总的来说,工具类中有三个方法 * 获取JwtToken,获取JwtToken中封装的信息,判断JwtToken是否存在 * 1. encode(),参数是=签发人,存在时间,一些其他的信息=。返回值是JwtToken对应的字符串 * 2. decode(),参数是=JwtToken=。返回值是荷载部分的键值对 * 3. isVerify(),参数是=JwtToken=。返回值是这个JwtToken是否存在 * */ public class JwtUtil { //创建默认的秘钥和算法,供无参的构造方法使用 private static final String defaultbase64EncodedSecretKey = "badbabe"; private static final SignatureAlgorithm defaultsignatureAlgorithm = SignatureAlgorithm.HS256; public JwtUtil() { this(defaultbase64EncodedSecretKey, defaultsignatureAlgorithm); } private final String base64EncodedSecretKey; private final SignatureAlgorithm signatureAlgorithm; public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) { this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes()); this.signatureAlgorithm = signatureAlgorithm; } /* *这里就是产生jwt字符串的地方 * jwt字符串包括三个部分 * 1. header * -当前字符串的类型,一般都是"JWT" * -哪种算法加密,"HS256"或者其他的加密算法 * 所以一般都是固定的,没有什么变化 * 2. payload * 一般有四个最常见的标准字段(下面有) * iat:签发时间,也就是这个jwt什么时候生成的 * jti:JWT的唯一标识 * iss:签发人,一般都是username或者userId * exp:过期时间 * * */ public String encode(String iss, long ttlMillis, Map claims) { //iss签发人,ttlMillis生存时间,claims是指还想要在jwt中存储的一些非隐私信息 if (claims == null) { claims = new HashMap<>(); } long nowMillis = System.currentTimeMillis(); JwtBuilder builder = Jwts.builder() .setClaims(claims) .setId(UUID.randomUUID().toString())//2. 这个是JWT的唯一标识,一般设置成唯一的,这个方法可以生成唯一标识 .setIssuedAt(new Date(nowMillis))//1. 这个地方就是以毫秒为单位,换算当前系统时间生成的iat .setSubject(iss)//3. 签发人,也就是JWT是给谁的(逻辑上一般都是username或者userId) .signWith(signatureAlgorithm, base64EncodedSecretKey);//这个地方是生成jwt使用的算法和秘钥 if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis);//4. 过期时间,这个也是使用毫秒生成的,使用当前时间+前面传入的持续时间生成 builder.setExpiration(exp); } return builder.compact(); } //相当于encode的方向,传入jwtToken生成对应的username和password等字段。Claim就是一个map //也就是拿到荷载部分所有的键值对 public Claims decode(String jwtToken) { // 得到 DefaultJwtParser return Jwts.parser() // 设置签名的秘钥 .setSigningKey(base64EncodedSecretKey) // 设置需要解析的 jwt .parseClaimsJws(jwtToken) .getBody(); } //判断jwtToken是否合法 public boolean isVerify(String jwtToken) { //这个是官方的校验规则,这里只写了一个"校验算法",可以自己加 Algorithm algorithm = null; switch (signatureAlgorithm) { case HS256: algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey)); break; default: throw new RuntimeException("不支持该算法"); } JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(jwtToken); // 校验不通过会抛出异常 //判断合法的标准:1. 头部和荷载部分没有篡改过。2. 没有过期 return true; } public static void main(String[] args) { JwtUtil util = new JwtUtil("tom", SignatureAlgorithm.HS256); //以tom作为秘钥,以HS256加密 Map map = new HashMap<>(); map.put("username", "tom"); map.put("password", "123456"); map.put("age", 20); String jwtToken = util.encode("tom", 30000, map); System.out.println(jwtToken); util.decode(jwtToken).entrySet().forEach((entry) -> { System.out.println(entry.getKey() + ": " + entry.getValue()); }); } } ResponseUtil、ResultUtil、repository等不做展示。需要的可以联系我,这个是基础性的东西依赖 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-security com.auth0 java-jwt 3.11.0 io.jsonwebtoken jjwt 0.9.1 org.springframework.security spring-security-jwt 1.0.10.RELEASE


redmiNote11TPro主摄详解卢总微博Note11TPro不仅性能强,影像也是稳扎稳打搭载6400万像素高清镜头,两个字清!晰!超大底11。72英寸超大感光元件,进光量更大大像素1。6m融合大像素,带来出色夜景币安16亿美元LUNA归零,赵长鹏1200万枚UST补偿小散近日时刻关注UST脱钩LUNA崩盘导致全球币圈震荡议题的CZ,稍早再发豪语,宣布将以身作则,把币安于2018年领投Terra区块链所获得的1,500万枚LUNA质押所获的1,200PLC和单片机的区别有哪些?单片机具有结构简单使用方便价格便宜等优点,一般用于弱电控制。PLC是专门为工业现场的自动化控制而设计的,今天小万老师对这两种控制系统进行比较。1。从使用者学习掌握的角度进行比较单片数字政府(智慧政务)概念股数字经济2022年4月19日,中央全面深化改革委员会第二十五次会议,审议通过了关于加强数字政府建设的指导意见浪潮软件领先的政企数字化转型解决方案服务商,长期致力于智慧政府解决方案与服务供应95后卖光账号!曾在亚马逊年销8亿受亚马逊封号潮波及,去年被封了200多个账号的卖家现在怎么样了?去年6月,95后亚马逊卖家田为因操纵评论,违反平台政策,被封了200多个账号。7月份,他准备了5个账号继续经营原来的快手发布2022直播运营白皮书助力616实在购物节火力开幕5月20日,快手616实在购物节正式启动。作为快手电商一年一度的年中大促,本次616实在购物节以实在人,实在货,实在价为主题,将一直持续至6月18日。期间,快手平台将通过升级玩法和996是谁提出来的?1。996是一家公司提出的。2996工作制是违反劳动法的延长法定工作时间的工作制度,是早晨9点上班晚上9点下班中午和晚上休息1小时(或以上)共工作10小时以上每周工作6天的工作制度京东估值过低但不值得购买摘要京东股票和许多中国股票年初至今已下跌23,上涨潜力达81。然而,我将JD评为持有,因为我认为该公司的长期增长和发展存在重大不利因素,我将在文章中详细阐述。我认为,投资者可以考虑21。28万起售的比亚迪海豹能把特斯拉Model3拉下马吗?中国新能源市场有两款很重要的轿车,分别是特斯拉Model3和比亚迪汉,这两款车价格相近,月销均能破万,成为很多新能源买家纠结的车型。但详细对比发现,这两款车其实并不一样,特斯拉Mo特斯拉又召回保时捷终妥协恒驰5仍在推进中E周大事记,周周有新瓜。本周特斯拉在国内又双叒召回了,当然这次召回并不是什么大毛病,毕竟特斯拉不发通知,您都可能发现不了。上周被抖音大V围殴的保时捷,在本周三终于妥协了,发布了最新4月新能源销量出炉,国产百花齐放比亚迪最大赢家随着基础设施的不断普及,以及新能源车型的技术不断提升,如今选择新能源车型的消费者越来越多,一些新能源车型甚至出现了供不应求的情况没有优惠,排队下订,有的时候等车都要等好几个月,真是
如何评价小米Mix4?搭载888的它都有哪些变化?好久不见甚是想念小伙伴们,这里是池鱼数码近些天发布的小米Mix4,想必大家耳熟能详!今天咱们一起来聊一聊这款小米Mix4!全面屏时代早已来到,随着社会,科技不断发展创新,手机数码行苹果计划发布多款产品,M1XMacBookPro将于11月上市Apple将在今年秋季举办多场活动,第一场活动可能会在9月份推出iPhone13。去年,由于全球健康危机和生产限制,iPhone12阵容直到10月才公布。2020年9月的活动不是专苹果官翻iPadPro开卖节省2000多上个周末,苹果在中国官网上线了2020款iPadPro官翻国行版本。新产品与全新版本的机型配置完全一致,价格还是很合适的,其中128GB版售价5999元256GB版售价6699元5无需供电Wi1080P7问世近年来,监控摄像头在城市交通等公共领域得到了空前广泛的应用,与此同时,家用商用监控也迎来了蓬勃发展。从有线监控到无线监控,从监控到4G监控,创新设计的网络监控不断刷新着人们的认知,小米MIX4预约量突破50万4999的价格卖疯了经过一个周末,数据显示小米MIX4预约量突破了52万。作为一款售价为4999元的新机,小米MIX4开售瞬间便被抢购一空,52万的预约量也意味着米粉对这款旗舰有着极高的购买热情,而在实测绿厂饼干充电器,小巧便携只是小优点,199入手太值了随着手机平板和笔记本等数码产品的普及,充电器早已成为每天都会用到,并且必不可少的物品。想要数码产品迅速回血,充电器的功率直接影响使用体验,但考虑到便携性的话,充电器体积和重量又成为暴风集团被执行近3亿元,曾欠2。6亿仅还244万,老板冯鑫仍在狱中8月16日消息,据天眼查App显示,昔日妖股之王暴风集团股份有限公司(下称暴风集团)新增被执行人信息,执行标的2。87亿元,执行法院为深圳市中级人民法院。相关案由显示为金融借款合同搭载骁龙888,荣耀Magic3Pro成驭龙高手8月12日,荣耀Magic3系列正式发布,其中荣耀Magic3Pro以及荣耀Magic3至臻版都搭载了高通骁龙888Plus芯片,这颗全新的旗舰芯片在荣耀Magic3Pro上会有什雷军退还小米手机1收入!每人赠送1999元红包,米粉已上架闲鱼,1000元1999元不等中国经济周刊经济网讯近日,小米创始人雷军公布小米十周年感恩红包领取情况称,截至8月15日16时,已有96986位用户收到1999元红包,累计1。94亿。据悉,8月10日的2021年华为鸿蒙再登央视升级人数超过五千万!华为3亿小目标再更新在中国不断发展的道路上,总会遇到许多拦路虎,其中的美国就是最大的那只,中国在不断的发展强大起来,而美国就开始想方设法的进行阻拦,对中国的企业重拳出击,各种打压。中国在科技领域内有些三星S22系列少数设备配备AMD图像的Exynos处理器虽然三星才刚刚发布了两款GalaxyZ系列手机,而同样采用骁龙888处理器的GalaxyS21FE也预计会在短期内发布,但不少打算换机,尤其是密切关注手机新闻的消费者,可能都正在等