spring security集成jwt
1、pom依赖引入
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>4.0.3.RELEASE</version></dependency>
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>4.0.3.RELEASE</version></dependency>
2、基础配置
properties文件
rsaPrivateKey=MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIxgS3b5J9IRYlvEFIEeDeQCGkOI5pT+Nl3wNe0fdWIiw36g4sH1l2sLuTZ6bew9YRLIapude6ORGZF5UfNy9cor3N7n3ew/fXCEKVRC6Kg+cREm1rqyMjDc9NtfksXG4RGf7GNeoTmUDVStsnXvoLnzvrE7FvbB12XKfhTQDnSZAgMBAAECgYBcyET45SP5x/2/87EtymSaAP3FB5aIgiIDIwMxsKpQa/PVHZfjZWVonn4T0QYYsFUaKhe0tXmEGiLRMWQGSkTEGfZ5l7uRmrNZ0Nk9asu4/fyjwZNHYDDGAELU5R4WgLvO09PdVLG/uyIxXh9qg9y9OpYM4KoATnsH7t7TPdI5gQJBAMnB6nz18BKSsHX7qDkWWpxZUOZmcKIsZoaDlz1NkoAKrOuH8TYc75uwLhR17nOOI9kO+10FKlYQ/5+yUiQIE+kCQQCyHcU/0mlvvByrJsYJMDUMtDk5/BtCWD6UZan/X1GH2EHx1W5LuL+wyIr6CUrY5G3osVU5ZvLly4zTFRQHr10xAkEAqFJT4zT7uUMQXR47VoVDyzTovY+xYFtSnd6jCs3w70n4wfeEUfUKIgV2LDPHYDixx6EsLIrmqy87VGxdAxqKIQJAe2bOyvnfXK9KeXWCjMkeZ+/RGhBFXoC+0pdg4PHMDb7RaVgCc2nLPRKj3rljZsNUNnvt3LgrnvOYXIHk/7IKcQJAGakaB+211CbKnIHtjxSsg07EiM3dZVA1jrXTbJ6NuhURZTIOR0YJsVdWoEbwBLv/STTvc7+G+wo1yuzzBAse+w==
rsaPublicKey=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMYEt2+SfSEWJbxBSBHg3kAhpDiOaU/jZd8DXtH3ViIsN+oOLB9ZdrC7k2em3sPWESyGqbnXujkRmReVHzcvXKK9ze593sP31whClUQuioPnERJta6sjIw3PTbX5LFxuERn+xjXqE5lA1UrbJ176C5876xOxb2wddlyn4U0A50mQIDAQAB
# AES密码加密私钥(Base64加密)
encryptAESKey=V2FuZzkyNjQ1NGRTQkFQSUpXVA==
# JWT认证加密私钥(Base64加密)
encryptJWTKey=U0JBUElKV1RkV2FuZzkyNjQ1NA==
# AccessToken过期时间-5分钟-5*60(秒为单位)
accessTokenExpireTime=300
# RefreshToken过期时间-30分钟-30*60(秒为单位)
refreshTokenExpireTime=1800
# Shiro缓存过期时间-5分钟-5*60(秒为单位)(一般设置与AccessToken过期时间一致)
shiroCacheExpireTime=10
# 文件缓存地址
fileCachePath=D:\\download\\kmair\\contract\\file_backup
loginUrl=/user/loginByUsernameAndPassword
读取配置类(@Data是lombok注解)
@Data
public class SysProperties {private String rsaPublicKey;private String rsaPrivateKey;private String encryptAESKey;private String encryptJWTKey;private int accessTokenExpireTime;private int refreshTokenExpireTime;private int shiroCacheExpireTime;private String fileCachePath;private String loginUrl;
}
@Data
@EnableAutoConfiguration
@Configuration
@PropertySource("classpath:config.properties")
public class SysConfig {@Value("${rsaPublicKey}")private String rsaPublicKey;@Value("${rsaPrivateKey}")private String rsaPrivateKey;@Value("${encryptAESKey}")private String encryptAESKey;@Value("${encryptJWTKey}")private String encryptJWTKey;@Value("${accessTokenExpireTime}")private int accessTokenExpireTime;@Value("${refreshTokenExpireTime}")private int refreshTokenExpireTime;@Value("${shiroCacheExpireTime}")private int shiroCacheExpireTime;@Value("${fileCachePath}")private String fileCachePath;@Value("${loginUrl}")private String loginUrl;@Bean(value = "sysProperties",name = "sysProperties")public SysProperties init(){SysProperties sysProperties = new SysProperties();sysProperties.setRsaPublicKey(rsaPublicKey);sysProperties.setRsaPrivateKey(rsaPrivateKey);sysProperties.setEncryptAESKey(this.encryptAESKey);sysProperties.setEncryptJWTKey(this.encryptJWTKey);sysProperties.setAccessTokenExpireTime(this.accessTokenExpireTime);sysProperties.setRefreshTokenExpireTime(this.refreshTokenExpireTime);sysProperties.setShiroCacheExpireTime(this.shiroCacheExpireTime);sysProperties.setFileCachePath(fileCachePath);sysProperties.setLoginUrl(loginUrl);return sysProperties;}
}
3、response工具类
这里有一个坑要注意避免,部分版本的fastjson在处理数据返回的时候,会调用response的getWrite方法,所以我们这里需要调用getOutputstream方法,不然会出现报错,提示getWrite已经被调用过,我的fastjson版本是1.2.70
/*** @author Mr.Wen* @version 1.0* @date 2021-08-11 15:34*/
@Slf4j
public class ResponseUtils {public static void out(HttpServletResponse response, HttpResult result){response.setStatus(HttpStatus.OK.value());//统一返回的JSON数据response.setContentType("application/json; charset=UTF-8");try (ServletOutputStream out = response.getOutputStream()) {out.write(JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8));} catch (IOException e) {log.error("输出数据异常", e);}}
}
4、常量类
package com.kmair.ky.contract.common.entity;/*** 常量* @author dolyw.com* @date 2018/9/3 16:03*/
public class Constant {private Constant() {}/*** redis-OK*/public static final String OK = "OK";/*** redis过期时间,以秒为单位,一分钟*/public static final int EXRP_MINUTE = 60;/*** redis过期时间,以秒为单位,一小时*/public static final int EXRP_HOUR = 60 * 60;/*** redis过期时间,以秒为单位,一天*/public static final int EXRP_DAY = 60 * 60 * 24;/*** redis-key-前缀-shiro:access_token:*/public static final String PREFIX_SHIRO_ACCESS_TOKEN = "shiro:access_token:";/*** redis-key-前缀-shiro:refresh_token_prefix:*/public static final String PREFIX_SHIRO_USER = "shiro:user_prefix:";/*** redis-key-前缀-shiro:refresh_token:*/public static final String PREFIX_SHIRO_REFRESH_TOKEN = "shiro:refresh_token:";/*** redis-key-前缀-security:user_details:*/public static final String PREFIX_SECURITY_USER_DETAILS = "security:user_details:";/*** JWT-account:*/public static final String ACCOUNT = "account";/*** jwt-存储权限信息*/public static final String AUTHORITIES = "authorities";/*** JWT-currentTimeMillis:*/public static final String CURRENT_TIME_MILLIS = "currentTimeMillis";/*** PASSWORD_MAX_LEN*/public static final Integer PASSWORD_MAX_LEN = 8;/*** 资源目录菜单标识*/public static final String MENU_CODE = "menu";}
5、用户信息类重写
package com.kmair.ky.contract.config.security;import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** @author Mr.Wen* @version 1.0* @date 2021-08-11 15:07*/
@Data
public class SecurityUserDetails implements UserDetails {private String username;private String passwrod;/*** 权限标识集合*/private List<String> permissions;public SecurityUserDetails() {}public SecurityUserDetails(String username, String passwrod, List<String> permissions) {this.username = username;this.passwrod = passwrod;this.permissions = permissions;}/*** 账号是否过期*/private Boolean isAccountExpired = true;/*** 账号是否锁定*/private Boolean isAccountLocked = true;/*** 密码是否过期*/private Boolean isCredentialsExpired = true;/*** 是否被禁用*/private Boolean isEnabled = true;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Collection<GrantedAuthority> authorities =new ArrayList<>();for (String permisson : permissions) {if(permisson!=null) {GrantedAuthority authority=new SimpleGrantedAuthority(permisson);authorities.add(authority);}}return authorities;}@Overridepublic String getPassword() {return passwrod;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return isAccountExpired;}@Overridepublic boolean isAccountNonLocked() {return isAccountLocked;}@Overridepublic boolean isCredentialsNonExpired() {return isCredentialsExpired;}@Overridepublic boolean isEnabled() {return isEnabled;}
}
6、用户信息查询类重写
这里要实现查询用户信息,关键就是实现UserDetailsService接口,内部实现,根据自己的需求来写
/*** @author Mr.Wen* @version 1.0* @date 2021-08-11 15:05*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate UserDataMapper userDataMapper;@Resourceprivate SysRoleMapper sysRoleMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {UserData userData = userDataMapper.selectByWorkId(username);List<SysRole> sysRoles = sysRoleMapper.selectByUserId(userData.getId());List<String> permissions = new ArrayList<>();for(SysRole sysRole:sysRoles){permissions.add(sysRole.getRoleCode());}return new SecurityUserDetails(userData.getUsername(),userData.getPassword(),permissions);}
}
7、授权过滤器,处理token
我使用了双token,这里也是根据自己的认真逻辑修改就好了
package com.kmair.ky.contract.config.security;import com.kmair.ky.contract.common.api.HttpResult;
import com.kmair.ky.contract.common.entity.Constant;
import com.kmair.ky.contract.config.common.SysProperties;
import com.kmair.ky.contract.utils.auth.JwtUtil;
import com.kmair.ky.contract.utils.cache.JedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.annotation.Resource;
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.List;/*** @author Mr.Wen* @version 1.0* @date 2021-08-11 15:20*/
@Component
public class SecurityAuthorizeFilter extends OncePerRequestFilter {@Resourceprivate UserDetailsServiceImpl userDetailsService;@Resourceprivate SysProperties sysProperties;@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain chain)throws IOException, ServletException {UsernamePasswordAuthenticationToken authentication=getAuthentication(request, response);if(authentication!=null){SecurityContextHolder.getContext().setAuthentication(authentication);}chain.doFilter(request, response);}private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request,HttpServletResponse response) {String currentUrl = request.getServletPath();if(currentUrl.equals(sysProperties.getLoginUrl())){return null;}//查看请求头是否有 tokenString token = request.getHeader("Authorization");if(!StringUtils.isEmpty(token)){String username = null;List<String> permissions = null;try {username = JwtUtil.getClaim(token, Constant.ACCOUNT);} catch (Exception e) {logger.error("token解析异常",e);ResponseUtils.out(response, HttpResult.customCode(4012,"token为空,请登录",null));return null;}// refreshToken过期,提示重新登陆if(!JedisUtil.exists(Constant.PREFIX_SHIRO_REFRESH_TOKEN+username)){ResponseUtils.out(response, HttpResult.customCode(4011,"token过期,请登录",null));return null;}//accessToken过期,refreshToken未过期,重新签发tokenif(!JedisUtil.exists(Constant.PREFIX_SHIRO_ACCESS_TOKEN+username)){SecurityUserDetails userDetails = null;if(!JedisUtil.exists(Constant.PREFIX_SECURITY_USER_DETAILS + username)){userDetails = (SecurityUserDetails) userDetailsService.loadUserByUsername(username);}else{userDetails = (SecurityUserDetails)JedisUtil.getObject(Constant.PREFIX_SECURITY_USER_DETAILS + username);}// 拿到refreshToken签发的日期String refreshToken = request.getHeader("refreshToken");try {String timeStr = JwtUtil.getClaim(refreshToken, Constant.CURRENT_TIME_MILLIS);long time = Long.valueOf(timeStr);long currentTimeMillis = System.currentTimeMillis();String newAccessToken = JwtUtil.sign(username,String.valueOf(currentTimeMillis));// 在使用期间,新的accessToken时间大于refreshToke时间,表示用户正在使用,且refreshToken即将过期,两个token都要重新签发// 利用currentTimeMillis和token的时间,也可以判断用户是否很久没有操作了if((currentTimeMillis + sysProperties.getAccessTokenExpireTime()) > (time+sysProperties.getRefreshTokenExpireTime())){JedisUtil.delKey(Constant.PREFIX_SECURITY_USER_DETAILS + username);JedisUtil.delKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN+username);String newRefreshToken = JwtUtil.sign(username,String.valueOf(currentTimeMillis));JedisUtil.setObject(Constant.PREFIX_SHIRO_REFRESH_TOKEN + username, newRefreshToken, sysProperties.getRefreshTokenExpireTime());JedisUtil.setObject(Constant.PREFIX_SECURITY_USER_DETAILS + username, userDetails, sysProperties.getRefreshTokenExpireTime());response.setHeader("NWE_REFRESH_TOKEN",newRefreshToken);}JedisUtil.setObject(Constant.PREFIX_SHIRO_ACCESS_TOKEN + username,newAccessToken,sysProperties.getAccessTokenExpireTime());response.setHeader("NWE_ACCESS_TOKEN",newAccessToken);return new UsernamePasswordAuthenticationToken(userDetails, null, null);} catch (Exception e) {e.printStackTrace();}}return null;}else{ResponseUtils.out(response, HttpResult.customCode(4012,"token为空,请登录",null));return null;}}
}
8、密码处理器
package com.kmair.ky.contract.config.security;import com.kmair.ky.contract.config.common.SysProperties;
import com.kmair.ky.contract.utils.security.PBECoder;
import com.kmair.ky.contract.utils.security.RsaEncrypt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** spring security 密码处理的实现* @author Mr.Wen* @version 1.0* @date 2021-08-12 13:51*/
@Component
@Slf4j
public class SecurityPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword){if (rawPassword == null) {throw new IllegalArgumentException("rawPassword cannot be null");}//将前端加密传输的密码解密,再使用PBE加密,和数据库中存储的密码做对比String result;try {result = PBECoder.encrypt(rawPassword.toString()).replaceAll("[\r|\n]","");} catch (Exception e) {log.error("用户密码解密出现异常",e);throw new RuntimeException("对用户输入的密码进行rsa解密出现异常",e);}return result;}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {if (rawPassword == null) {throw new IllegalArgumentException("rawPassword cannot be null");}if (encodedPassword == null || encodedPassword.length() == 0) {return false;}return encode(rawPassword).equals(encodedPassword);}
}
9、security运行配置
package com.kmair.ky.contract.config.security;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.annotation.Resource;/*** @author Mr.Wen* @version 1.0* @date 2021-08-11 15:16*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate UserDetailsService userDetailsService;@Resourceprivate SecurityAuthorizeFilter customAuthorizeFilter;@Resourceprivate SecurityPasswordEncoder securityPasswordEncoder;@Overrideprotected void configure(HttpSecurity http) throws Exception {http//必须放在UsernamePasswordAuthenticationFilter之前,如果请求头中替代有Token 就直接放行,没有的话 再进行用户名密码登陆.addFilterBefore(customAuthorizeFilter, UsernamePasswordAuthenticationFilter.class)//异常处理器.exceptionHandling().and()// 授权请求.authorizeRequests()//permitAll 不需要任何授权 就可以访问//authenticated 需要登陆了 才能进行访问
// .antMatchers("/**").authenticated().antMatchers("/user/loginByUsernameAndPassword","/user/refreshToken/","/user/getUserInfoByToken").permitAll().and()//修改session策略 无状态应用 STATELESS 因为没用到session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//解决跨域访问.cors().and().csrf().disable();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(securityPasswordEncoder);}@Overridepublic void configure(WebSecurity web){web.ignoring().antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs", "/v2/**", "/webjars/**");}@Beanpublic AuthenticationManager getAuthenticationManager() throws Exception{return super.authenticationManager();}
}
10、登录,退出接口
这个类的login方法涉及到一个加密解密的算法,前端传递的用户密码信息都是用这个算法加密解密的,换成自己的实现即可;根据用户令牌获取用户权限的接口自己实现就好;退出没有调用spring security的方法,自己实现了一个,另一种方法就是使用默认的退出登录的接口,然后自己实现一个LogoutSuccessHandler,在这里面处理退出的逻辑即可
package com.kmair.ky.contract.system.login.controller;import com.kmair.ky.contract.common.api.HttpResult;
import com.kmair.ky.contract.common.entity.Constant;
import com.kmair.ky.contract.config.common.SysProperties;
import com.kmair.ky.contract.config.security.SecurityUserDetails;
import com.kmair.ky.contract.system.login.service.impl.LoginServiceImpl;
import com.kmair.ky.contract.system.user.entity.UserData;
import com.kmair.ky.contract.utils.auth.JwtUtil;
import com.kmair.ky.contract.utils.cache.JedisUtil;
import com.kmair.ky.contract.utils.security.RsaEncrypt;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
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.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;/*** 登录* @author Mr.Wen*/
@RestController
@RequestMapping("/user")
@Api(tags = "用户登录接口")
@Slf4j
public class LoginController {@Resourceprivate SysProperties sysProperties;@Resourceprivate LoginServiceImpl loginService;@Resourceprivate RsaEncrypt rsaEncrypt;@Resourceprivate AuthenticationManager authenticationManager;@RequestMapping(value = "/loginPage",method = RequestMethod.GET)@ApiOperation("进入登录页面")public String loginPage() {return "login page";}/*** 通过用户名密码登录* @param user 用户信息* @param response 请求的响应* @return 返回登录状态*/@PostMapping("/loginByUsernameAndPassword")@ApiOperation("使用用户名登录")public HttpResult loginByUsernameAndPassword(@RequestBody @ApiParam(name="user",value="用户信息",required=true) UserData user, HttpServletResponse response) {try {// 查询数据库中的帐号信息String username = rsaEncrypt.decrypt(user.getUsername(),sysProperties.getRsaPrivateKey());String password = rsaEncrypt.decrypt(user.getPassword(),sysProperties.getRsaPrivateKey());// 清空redisJedisUtil.delKey(Constant.PREFIX_SHIRO_ACCESS_TOKEN + username);JedisUtil.delKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN + username);JedisUtil.delKey(Constant.PREFIX_SECURITY_USER_DETAILS + username);UsernamePasswordAuthenticationToken authRequest =new UsernamePasswordAuthenticationToken(username, password);Authentication authentication = authenticationManager.authenticate(authRequest);SecurityContextHolder.getContext().setAuthentication(authentication);SecurityUserDetails userDetails = (SecurityUserDetails) authentication.getPrincipal();//存储tokenString currentTime = String .valueOf(System.currentTimeMillis());String accessToken = JwtUtil.sign(userDetails.getUsername(),currentTime);String refreshToken = JwtUtil.sign(userDetails.getUsername(),currentTime);JedisUtil.setObject(Constant.PREFIX_SHIRO_ACCESS_TOKEN + username,accessToken,sysProperties.getAccessTokenExpireTime());JedisUtil.setObject(Constant.PREFIX_SHIRO_REFRESH_TOKEN + username, refreshToken, sysProperties.getRefreshTokenExpireTime());JedisUtil.setObject(Constant.PREFIX_SECURITY_USER_DETAILS + username, userDetails, sysProperties.getRefreshTokenExpireTime());Map<String,String> ret = new HashMap<>(4);ret.put("accessToken",accessToken);ret.put("refreshToken",refreshToken);response.setHeader("Access-Control-Expose-Headers", "Authorization");return HttpResult.success("登陆成功",ret);}catch (Exception e){e.printStackTrace();if(e instanceof BadCredentialsException){return HttpResult.error("登陆失败--密码错误");} else if (e instanceof UnsupportedEncodingException){return HttpResult.error("登陆失败--不支持的编码");}else if(e instanceof InvalidKeySpecException){return HttpResult.error("登陆失败--rsa的key不合法");}else{return HttpResult.error("登陆失败,请联系管理员");}}}/*** 通过token获取用户信息* @param request 请求体* @return 用户信息(用户名,角色,资源权限)*/@GetMapping("/getUserInfoByToken")@ApiOperation("根据token查询用户权限信息")public HttpResult getUserInfoByToken(HttpServletRequest request) throws Exception{String authorization = request.getHeader("Authorization");// 解密获得AccountString username = JwtUtil.getClaim(authorization, Constant.ACCOUNT);Map<String, Object> userInfo = loginService.getUserInfoByUsername(username);return HttpResult.success(userInfo);}/*** 退出登录* @param request 请求体* @return 操作状态*/@PostMapping("/logout")@ApiOperation("退出登录")public HttpResult logout(HttpServletRequest request) throws Exception{String authorization = request.getHeader("Authorization");// 解密获得AccountString username = JwtUtil.getClaim(authorization, Constant.ACCOUNT);Boolean accessTokenExist = JedisUtil.exists(Constant.PREFIX_SHIRO_ACCESS_TOKEN + username);if (accessTokenExist != null && accessTokenExist) {JedisUtil.delKey(Constant.PREFIX_SHIRO_ACCESS_TOKEN + username);}Boolean refreshTokenExist = JedisUtil.exists(Constant.PREFIX_SHIRO_REFRESH_TOKEN + username);if (refreshTokenExist != null && refreshTokenExist) {JedisUtil.delKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN + username);}Boolean userDetailsExist = JedisUtil.exists(Constant.PREFIX_SECURITY_USER_DETAILS + username);if (userDetailsExist != null && userDetailsExist) {JedisUtil.delKey(Constant.PREFIX_SECURITY_USER_DETAILS + username);}HttpSession session = request.getSession(false);if (session != null) {session.invalidate();}SecurityContext context = SecurityContextHolder.getContext();context.setAuthentication(null);SecurityContextHolder.clearContext();return HttpResult.success("退出成功");}}
11、总结
spring security可以自定义loginSuccessHandler,logoutSuccessHandler还有对应的失败的以及其他的handler,总的来说功能还是比较强大,和shiro相比,二者能实现功能都差不多,但是spring security依赖于spring容器,而且spring security对新手不太友好,总的来说,可以用shiro就没必要使用spring security。
spring security集成jwt相关推荐
- 学成在线-第16天-讲义- Spring Security Oauth2 JWT RSA加解密
学成在线-第16天-讲义- Spring Security Oauth2 JWT 1 用户认证需求分析 1.1 用户认证与授权 截至目前,项目已经完成了在线学习功能,用户通过在线学习页面点播视频进 ...
- 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权
一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...
- Spring Security Oauth2 JWT 实现用户认证授权功能
Spring Security Oauth2 JWT 一 用户认证授权 1. 需求分析 1.1 用户认证与授权 什么是用户身份认证? 用户身份认证即用户去访问系统资源时系统要求验证用户的身份信息,身份 ...
- Spring Security整合JWT,实现单点登录,So Easy~!
前面整理过一篇 SpringBoot Security前后端分离,登录退出等返回json数据,也就是用Spring Security,基于SpringBoot2.1.4 RELEASE前后端分离的情况 ...
- 【Spring】Spring Security OAuth2 JWT 认证
1.概述 Spring Security OAuth2 JWT 认证服务器配置 Spring Security OAuth2 JWT 资源服务器配置 Spring Security OAuth2 Re ...
- 基于Spring Security和 JWT的权限系统设计
2019独角兽企业重金招聘Python工程师标准>>> 写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会 ...
- Spring Security和 JWT两大利器来打造一个简易的权限系统。
写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求.在Java EE领域,成熟的安全框架解决方案一般有 A ...
- 基于Spring Security与JWT实现单点登录
基于RBAC的权限管理 RBAC(Role-Based Access Control):基于角色的访问控制 当前项目中,RBAC具体的表现为: 管理员表:ams_admin 角色表:ams_role ...
- Spring security 集成 JustAuth 实现第三方授权登录
Spring security 集成 JustAuth 实现第三方授权登录脚手架: 一.特性 spring security 集成 JustAuth 实现第三方授权登录 : 此项目从 用户管理脚手架( ...
- Spring Security 集成 Authing CAS 认证(一)
01 集成介绍 单点登录 (Single Sign On),英文名称缩写 SSO,意思是在多系统的环境中,登录单方系统,就可以无须再次登录,访问相关受信任的系统.也就是说,只要登录一次单体系统即可. ...
最新文章
- 在线作图|小基因组——线粒体基因组圈图
- 一种高效的可变行高列表行定位算法
- 在不同制作基础上该如何选择网页制作软件?
- 黑马28期Android全套视频无加密完整版
- 【Linux】【服务器】 CentOS7下卸载MySQL详细过程步骤
- JQuery绑定事件 时如何传递参数
- 超全面的freemarker教程
- element 往node里面增加属性值_HashMap加载因子为何0.75,为何初始化值2的指数幂,底层解析...
- 通过SPANN方式将Spring&Quartz与自定义注释集成
- 初学JavaWeb,前端css要不要了解一下啊?一文学会JavaWeb中css的简单应用
- Object-C,NSArraySortTest,数组排序3种方式
- CF888E Maximum Subsequence(meet in the middle)
- 看看30万码农怎么评论:培训出来的程序员真的很渣吗?
- matlab 三维点 边界曲面,不用matlab如何从散点数据绘制极坐标三维曲面图?
- arm 服务器优势,零的突破 戴尔正式宣布基于ARM架构服务器
- html的跳转页面代码
- OtherSoftwares
- ue设置注释快捷键_UE编辑器快捷键大全 UltraEdit快捷键有哪些
- matlab中进行太阳能电池模型,基于Matlab的光伏发电系统仿真研究
- Ubuntu系统报错
热门文章
- 密钥配送问题解决方法
- 经典网页设计:30个独具匠心的单页网站设计案例
- 域名更换是什么意思?
- unbuntu 安装vscode
- oracle 三表连接 join,三个表innerjoin 如何用inner join关联三张表
- Win10免费升级 Win11 有时间限制
- facebook应用中_如何从Facebook应用程序的快捷方式栏中删除图标
- Python自动化 requests 库:发送 form-data 格式的 http 请求
- alert(1) (haozi.me)靶场练习
- 【C++】日期类题目总结