日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

案例代码:springboot+springsecurity+redis设置新登录后踢出前一个登录用户

發布時間:2024/9/30 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 案例代码:springboot+springsecurity+redis设置新登录后踢出前一个登录用户 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • pojo層
    • security 配置類
    • security工具類
    • controller:
    • service:

pojo層

@Data @AllArgsConstructor @NoArgsConstructor public class OnlineUser {private String userName;private String nickName;private String job;private String browser;private String ip;private String address;private String key;private Date loginTime;}

security 配置類

@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {private final TokenProvider tokenProvider;private final CorsFilter corsFilter;private final JwtAuthenticationEntryPoint authenticationErrorHandler;private final JwtAccessDeniedHandler jwtAccessDeniedHandler;private final ApplicationContext applicationContext;public SecurityConfig(TokenProvider tokenProvider, CorsFilter corsFilter, JwtAuthenticationEntryPoint authenticationErrorHandler, JwtAccessDeniedHandler jwtAccessDeniedHandler, ApplicationContext applicationContext) {this.tokenProvider = tokenProvider;this.corsFilter = corsFilter;this.authenticationErrorHandler = authenticationErrorHandler;this.jwtAccessDeniedHandler = jwtAccessDeniedHandler;this.applicationContext = applicationContext;}@BeanGrantedAuthorityDefaults grantedAuthorityDefaults() {// 去除 ROLE_ 前綴return new GrantedAuthorityDefaults("");}@Beanpublic PasswordEncoder passwordEncoder() {// 密碼加密方式return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 搜尋匿名標記 url: @AnonymousAccessMap<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();Set<String> anonymousUrls = new HashSet<>();for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {HandlerMethod handlerMethod = infoEntry.getValue();AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);if (null != anonymousAccess) {anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());}}httpSecurity// 禁用 CSRF.csrf().disable().addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)// 授權異常.exceptionHandling().authenticationEntryPoint(authenticationErrorHandler).accessDeniedHandler(jwtAccessDeniedHandler)// 防止iframe 造成跨域.and().headers().frameOptions().disable()// 不創建會話.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 靜態資源等等.antMatchers(HttpMethod.GET,"/*.html","/**/*.html","/**/*.css","/**/*.js","/webSocket/**").permitAll()// swagger 文檔.antMatchers("/swagger-ui.html").permitAll().antMatchers("/swagger-resources/**").permitAll().antMatchers("/webjars/**").permitAll().antMatchers("/*/api-docs").permitAll().antMatchers("/v2/api-docs-ext").permitAll()//.antMatchers("/api/wxmp/**").permitAll()// 文件.antMatchers("/avatar/**").permitAll().antMatchers("/file/**").permitAll()// 阿里巴巴 druid.antMatchers("/druid/**").permitAll()// 放行OPTIONS請求.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()// 所有請求都需要認證.anyRequest().authenticated().and().apply(securityConfigurerAdapter());}private TokenConfigurer securityConfigurerAdapter() {return new TokenConfigurer(tokenProvider);} }

其中TokenProvider , JwtAuthenticationEntryPoint,JwtAccessDeniedHandler 均為自定義的類,代碼過多就不貼了

security工具類

public class SecurityUtils {public static UserDetails getUserDetails() {final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null) {throw new BadRequestException(HttpStatus.UNAUTHORIZED, "當前登錄狀態過期");}if (authentication.getPrincipal() instanceof UserDetails) {UserDetails userDetails = (UserDetails) authentication.getPrincipal();UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class);return userDetailsService.loadUserByUsername(userDetails.getUsername());}throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到當前登錄的信息");}/*** 獲取系統用戶名稱* @return 系統用戶名稱*/public static String getUsername() {final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null) {throw new BadRequestException(HttpStatus.UNAUTHORIZED, "當前登錄狀態過期");}UserDetails userDetails = (UserDetails) authentication.getPrincipal();return userDetails.getUsername();}/*** 獲取系統用戶id* @return 系統用戶id*/public static Long getUserId() {Object obj = getUserDetails();JSONObject json = new JSONObject(obj);return json.get("id", Long.class);} }

controller:

@RestController @RequestMapping("/auth") @Api(tags = "系統:系統授權接口") public class AuthController {@Value("${loginCode.expiration}")private Long expiration;@Value("${rsa.private_key}")private String privateKey;@Value("${single.login}")private Boolean singleLogin;private final SecurityProperties properties;private final RedisUtils redisUtils;private final UserDetailsService userDetailsService;private final OnlineUserService onlineUserService;private final TokenProvider tokenProvider;private final AuthenticationManagerBuilder authenticationManagerBuilder;public AuthController(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, OnlineUserService onlineUserService, TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {this.properties = properties;this.redisUtils = redisUtils;this.userDetailsService = userDetailsService;this.onlineUserService = onlineUserService;this.tokenProvider = tokenProvider;this.authenticationManagerBuilder = authenticationManagerBuilder;}@Log("用戶登錄")@ApiOperation("登錄授權")@AnonymousAccess// @AnonymousAccess是自定義注解,用于表示可以匿名訪問的方法@PostMapping(value = "/login")public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request) {// 密碼解密RSA rsa = new RSA(privateKey, null);String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));// 查詢驗證碼String code = (String) redisUtils.get(authUser.getUuid());// 清除驗證碼redisUtils.del(authUser.getUuid());if (StringUtils.isBlank(code)) {throw new BadRequestException("驗證碼不存在或已過期");}if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {throw new BadRequestException("驗證碼錯誤");}UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);SecurityContextHolder.getContext().setAuthentication(authentication);//上下文設置用戶權限// 生成令牌String token = tokenProvider.createToken(authentication);final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();// 保存在線信息onlineUserService.save(jwtUser, token, request);// 返回 token 與 用戶信息Map<String, Object> authInfo = new HashMap<String, Object>(2) {{put("token", properties.getTokenStartWith() + token);put("user", jwtUser);}};if (singleLogin) {//踢掉之前已經登錄的tokenonlineUserService.checkLoginOnUser(authUser.getUsername(), token);}return ResponseEntity.ok(authInfo);}@ApiOperation("獲取驗證碼")@GetMapping(value = "/code")public ResponseEntity<Object> getCode() {// 算術類型 https://gitee.com/whvse/EasyCaptchaArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);// 幾位數運算,默認是兩位captcha.setLen(2);// 獲取運算的結果String result = "";try {result = new Double(Double.parseDouble(captcha.text())).intValue() + "";} catch (Exception e) {result = captcha.text();}String uuid = properties.getCodeKey() + IdUtil.simpleUUID();// 保存redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);// 驗證碼信息Map<String, Object> imgResult = new HashMap<String, Object>(2) {{put("img", captcha.toBase64());put("uuid", uuid);}};return ResponseEntity.ok(imgResult);}@ApiOperation("退出登錄")@DeleteMapping(value = "/logout")public ResponseEntity<Object> logout(HttpServletRequest request) {onlineUserService.logout(tokenProvider.getToken(request));return new ResponseEntity<>(HttpStatus.OK);} }

service:

獲得在線用戶列表,與當前登錄的用戶進行匹配,如果匹配到相同的,則刪除之前的

/*** 檢測用戶是否在之前已經登錄,已經登錄踢下線* @param userName 用戶名*/public void checkLoginOnUser(String userName, String igoreToken) {List<OnlineUser> onlineUsers = getAll(userName, 0);if (onlineUsers == null || onlineUsers.isEmpty()) {return;}for (OnlineUser onlineUser : onlineUsers) {if (onlineUser.getUserName().equals(userName)) {try {String token = EncryptUtils.desDecrypt(onlineUser.getKey());if (StringUtils.isNotBlank(igoreToken) && !igoreToken.equals(token)) {this.kickOut(onlineUser.getKey());} else if (StringUtils.isBlank(igoreToken)) {this.kickOut(onlineUser.getKey());}} catch (Exception e) {log.error("checkUser is error", e);}}}}}/*** 踢出用戶* @param key /* @throws Exception /*/public void kickOut(String key) throws Exception {key = properties.getOnlineKey() + EncryptUtils.desDecrypt(key);redisUtils.del(key);}/*** 退出登錄* @param token /*/public void logout(String token) {String key = properties.getOnlineKey() + token;redisUtils.del(key);}

其中EncryptUtils.desDecrypt方法:

/*** 對稱解密*/public static String desDecrypt(String source) throws Exception {byte[] src = hex2byte(source.getBytes());DESKeySpec desKeySpec = getDesKeySpec(source);SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");SecretKey secretKey = keyFactory.generateSecret(desKeySpec);cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);byte[] retByte = cipher.doFinal(src);return new String(retByte);}

AnonymousAccess 注解的使用對應Security config的抽血方法configure:

@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// 搜尋匿名標記 url: @AnonymousAccessMap<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();Set<String> anonymousUrls = new HashSet<>();for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {HandlerMethod handlerMethod = infoEntry.getValue();AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);if (null != anonymousAccess) {anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());}}

總結

以上是生活随笔為你收集整理的案例代码:springboot+springsecurity+redis设置新登录后踢出前一个登录用户的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。