SpringBoot整合SpringSecurity+JWT

SpringBoot整合SpringSecurity+JWT

整合SpringSecurity步骤

  1. 编写拦截链配置类,规定security参数
  2. 拦截登录请求的参数,对该用户做身份认证。
  3. 通过登录验证的予以授权,这里根据用户对应的角色作为授权标识。

整合JWT步骤

  1. 编写JWTUtils,包括生成、验证JWT的方法。
  2. 编写登录认证过滤器,生成token,并将token中的payload添加到redis中
  3. 编写路由过滤器,可行的路由则放行
  4. 登录认证后生成token返回response

结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

依赖Jar

<!-- JWT-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>9.0.63</version>
</dependency>
<!--Redis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.6.8</version>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.1</version>
</dependency>
<!--Spring Security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>2.6.8</version>
</dependency>
<!--Spring data jpa-->
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId><version>2.6.4</version>
</dependency>
<!-- querydsl -->
<dependency><groupId>com.querydsl</groupId><artifactId>querydsl-jpa</artifactId><version>5.0.0</version>
</dependency>
<!-- Hibernate对jpa的支持包 -->
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>5.6.9.Final</version>
</dependency>
<!-- MySQL-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.13</version>
</dependency>
<!-- Druid-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.16</version>
</dependency>
<!--Spring Boot相关-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.6.8</version>
</dependency>
<!--Spring aspect Auditor审计功能需要-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.20</version>
</dependency>
<!--Hutool 快速开发工具包-->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.9</version>
</dependency>

配置文件yml

server:port: 8642
spring:application:name: spring-data-jpadatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/你的数据库?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: 你的账号password: 你的密码type: com.alibaba.druid.pool.DruidDataSourcedruid:# 下面为连接池的补充设置,应用到上面所有数据源中# 初始化大小,最小,最大initial-size: 5min-idle: 5max-active: 20jpa:database: MYSQLdatabase-platform: org.hibernate.dialect.MySQL5InnoDBDialectshow-sql: trueopen-in-view: truehibernate:ddl-auto: updatenaming:physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImplproperties:hibernate:enable_lazy_load_no_trans: trueredis:host: 127.0.0.1port: 6379password: 你的密码(没有不填)lettuce:pool:# 最大活动数量max-active: 8# 当池耗尽时,在引发异常之前,连接分配应该阻塞的最长时间。使用负值可以无限期阻止。max-wait: -1# 最大闲置时间,单位:smax-idle: 500# 超时关闭时间shutdown-timeout: 0

整合SpringSecurity

Spring Security权限配置类

/*** @author Evad.Wu* @Description SpringSecurity权限配置类* @date 2022-06-28*/
@Configuration
public class EvadSecurityConfig extends WebSecurityConfigurerAdapter {private AuthenticationManager authenticationManager;@Resource(name = "evadRedisTemplate")private RedisTemplate<String, Object> redisTemplate;@Resource(name = "userServiceImpl")private BaseUserDetailsService userDetailsService;/*** 认证** @param auth 认证管理器建造者*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService);}/*** 授权** @param http 安全*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 开启 HttpSecurity 配置http.authorizeRequests().antMatchers("/securityController/login").permitAll().antMatchers("/securityController/evadLogin").permitAll().antMatchers("/securityController/evadLogout").permitAll().antMatchers("/securityController/user").permitAll().antMatchers("/securityController/dev").access("hasAnyRole('DEV','MASTER')").antMatchers("/securityController/devAndUser").access("hasAnyRole('MASTER') or (hasRole('DEV') and hasRole('USER'))").antMatchers("/securityController/master").access("hasAnyRole('MASTER')")// 用户访问其它URL都必须认证后访问(登录后访问).anyRequest().authenticated()// 开启表单登录并配置登录接口.and().formLogin().loginProcessingUrl("/login").permitAll().and().logout().logoutUrl("/logout").addLogoutHandler(new EvadLogoutHandler()).invalidateHttpSession(true).deleteCookies("JSESSIONID", "XXL_JOB_LOGIN_IDENTITY").clearAuthentication(true).logoutSuccessUrl("/login").permitAll().and().exceptionHandling().accessDeniedHandler((request, response, e) -> {request.setAttribute("state", 403);request.setAttribute("errMsg", "抱歉,您没有权限访问!");request.getRequestDispatcher("/toErrorPage");})// 添加jwt验证.and().addFilter(new JwtLoginFilter(authenticationManager, redisTemplate)).addFilter(new JwtValidationFilter(authenticationManager, redisTemplate))// 不使用HttpSession.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().cors().and().csrf().disable();}/*** 加密规则*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 生成一个认证管理器bean* @return* @throws Exception*/@Bean(value = "authenticationManager")@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {this.authenticationManager = super.authenticationManagerBean();return authenticationManager;}
}

校验登录信息(继承UserDetailsService接口),并授权(根据roles)

/*** @Description 用户认证信息的顶级接口* @author Evad.Wu* @date 2022-06-28*/
public interface BaseUserDetailsService extends UserDetailsService {
}/*** @author Evad.Wu* @Description 登录时校验数据库中的密码* @date 2022-06-28*/
@Service
public class UserServiceImpl implements BaseUserDetailsService {@Resource(name = "userRepository")private UserRepository userRepository;@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {User user = Optional.ofNullable(userRepository.findFirstByUsername(s)).orElseGet(User::new);if (user.getPassword().isEmpty()) {throw new UsernameNotFoundException("用户不存在!");}return this.user2UserDetail(user);}private UserDetail user2UserDetail(User user) {UserDetail userDetail = new UserDetail();userDetail.setId(user.getId());userDetail.setPassword(user.getPassword());userDetail.setUserName(user.getUsername());userDetail.setUserRoles(this.role2Dto(user.getRoles()));Boolean visible = Optional.ofNullable(user.getVisible()).orElse(true);userDetail.setEnabled(visible);userDetail.setLocked(!visible);return userDetail;}private List<RoleDto> role2Dto(Set<Role> roleList) {List<RoleDto> roleDtolist = new ArrayList<>();for (Role role : roleList) {RoleDto roleDto = new RoleDto();roleDto.setId(role.getId());roleDto.setRoleName(role.getRoleName());roleDto.setRoleCode(role.getRoleCode());roleDtolist.add(roleDto);}return roleDtolist;}
}

UserDetail 校验登录信息的对象(实现UserDetails)

/*** @author Evad.Wu* @Description 用户信息转换类* @date 2022-06-28*/
@Data
@NoArgsConstructor
@JsonIgnoreProperties({"username", "password", "enabled", "accountNonExpired", "accountNonLocked", "credentialsNonExpired", "authorities"})
public class UserDetail implements UserDetails {@Serialprivate static final long serialVersionUID = -2028119927623038905L;private Long id;private String userName;private String password;private Boolean enabled;private Boolean locked;private List<RoleDto> userRoles;private List<SimpleGrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = new ArrayList<>();for (RoleDto role : userRoles) {authorities.add(new SimpleGrantedAuthority(role.getRoleCode()));}return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return userName;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return !locked;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return enabled;}
}

整合JWT

JWTUtils工具类

注意:SIGNATURE是生成token的公钥,当外部token进来时需要公钥解密。

/*** @author Evad.Wu* @Description JWT 工具类* @date 2023-01-15*/
public class JWTUtils {/*** 生成token*/public static <T extends UserDetails> String createToken(T principal, Long expire) {JwtBuilder jwtBuilder = Jwts.builder();Map<String, Object> headerParams = new HashMap<>(16);headerParams.put("typ", "JWT");headerParams.put("alg", SignatureAlgorithm.HS256.getValue());Map<String, Object> claims = new HashMap<>(16);UserDetail user = (UserDetail) principal;claims.put("id", user.getId());claims.put("username", principal.getUsername());claims.put("role", user.getUserRoles());Date exp = new Date(System.currentTimeMillis() + expire);claims.put("exp", exp);return jwtBuilder.setHeader(headerParams).setIssuer(principal.getUsername()).setIssuedAt(new Date()).setClaims(claims).setExpiration(exp).signWith(SignatureAlgorithm.HS256, EvadSecretConstant.SIGNATURE).compact();}/*** 解析token** @param token 令牌* @return 解析结果*/public static boolean checkToken(String token) {JwtParser jwtParser = Jwts.parser();jwtParser.setSigningKey(EvadSecretConstant.SIGNATURE);try {jwtParser.parse(token);return true;} catch (ExpiredJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) {e.printStackTrace();}return false;}/*** 解析token** @param token     令牌* @param sercetKey 用户认证秘钥* @return 用户认证信息参数*/public static Claims verifyToken(String token, String sercetKey) {return Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey)).parseClaimsJws(token).getBody();}
}

JWT登录认证过滤器

/*** @author Evad.Wu* @Description jwt用户信息认证 过滤器* @date 2023-01-16*/
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {/*** 获取授权管理*/private final AuthenticationManager authenticationManager;private final RedisTemplate<String, Object> redisTemplate;public JwtLoginFilter(AuthenticationManager authenticationManager, RedisTemplate<String, Object> redisTemplate) {this.authenticationManager = authenticationManager;this.redisTemplate = redisTemplate;// 指定一个路由作为登录认证的入口super.setFilterProcessesUrl("/securityController/evadLogin");}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {Authentication authentication;try {BufferedReader br = request.getReader();StringBuilder body = new StringBuilder();String str;while ((str = br.readLine()) != null) {body.append(str);}LoginVo loginVo = JSONObject.parseObject(body.toString(), LoginVo.class);//先得到前端传入的账号密码Authentication对象UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());//AuthenticationManager authentication进行用户认证authentication = authenticationManager.authenticate(authenticationToken);System.out.println("authencation: " + authentication);if (Optional.ofNullable(authentication).isEmpty()) {response.setCharacterEncoding("UTF-8");response.getWriter().print("登录失败!");return null;}return authentication;} catch (IOException e) {logger.error(e.getMessage());}return null;}@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) {UserDetail userDetail = (UserDetail) authResult.getPrincipal();String jwtToken = JWTUtils.createToken(userDetail, 30 * 60 * 1000L);response.addHeader("token", jwtToken);//把完整的用户信息存入redis userid作为keylog.info("token: " + jwtToken);redisTemplate.opsForValue().set("login-" + userDetail.getId(), userDetail);}@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {response.setCharacterEncoding("UTF-8");response.getWriter().print("登录失败!");}
}

JWT token校验过滤器

/*** @author Evad.Wu* @Description jwt验证令牌 过滤器* @date 2023-01-16*/
@Slf4j
public class JwtValidationFilter extends BasicAuthenticationFilter {private final RedisTemplate<String, Object> redisTemplate;public JwtValidationFilter(AuthenticationManager authenticationManager, RedisTemplate<String, Object> redisTemplate) {super(authenticationManager);this.redisTemplate = redisTemplate;}/*** 过滤请求验证** @param request     请求体* @param response    响应体* @param filterChain 请求过滤链* @throws IOException      IO异常* @throws ServletException servlet异常*/@Overrideprotected void doFilterInternal(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("token");if (Optional.ofNullable(token).isEmpty()) {filterChain.doFilter(request, response);return;}response.setHeader("token", token);Claims claims = JWTUtils.verifyToken(token, EvadSecretConstant.SIGNATURE);Long id = claims.get("id", Long.class);Date exp = claims.getExpiration();UserDetail userDetail = (UserDetail) redisTemplate.opsForValue().get("login-" + id);System.out.println("过期时间:" + exp);log.info("解析到的用户: " + userDetail);if (Optional.ofNullable(userDetail).isEmpty()) {throw new RuntimeException("用户未登录");}// 存入SecurityContextHolder, 其他filter会通过这个来获取当前用户信息// 获取权限信息封装到authentication中UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken= new UsernamePasswordAuthenticationToken(userDetail, null, userDetail.getAuthorities());SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);// 放行filterChain.doFilter(request, response);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/28376.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

修复git diff正文中文乱码

Linux git diff正文中文乱码 在命令行下输入以下命令&#xff1a; $ git config --global core.quotepath false # 显示 status 编码 $ git config --global gui.encoding utf-8 # 图形界面编码 $ git config --global i18n.commit.encoding utf-8 # …

状态模式:游戏、工作流引擎中常用的状态机是如何实现的?

从今天起&#xff0c;我们开始学习状态模式。在实际的软件开发中&#xff0c;状态模式并不是很常用&#xff0c;但是在能够用到的场景里&#xff0c;它可以发挥很大的作用。从这一点上来看&#xff0c;它有点像我们之前讲到的组合模式。 可以简短的回顾一下组合模式&#xff1a…

本地appserv外挂网址如何让外网访问?快解析端口映射

一、appserv是什么&#xff1f; AppServ 是 PHP 网页架站工具组合包&#xff0c;作者将一些网络上免费的架站资源重新包装成单一的安装程序&#xff0c;以方便初学者快速完成架站&#xff0c;AppServ 所包含的软件有&#xff1a;Apache[、Apache Monitor、PHP、MySQL、phpMyAdm…

mybatis基础

1.搭建环境 2.单参模糊查询 3.parameter语法 4.多参实战 4.1参数为对象 4.2参数为Map 4.3参数为注解标识 5.增删改 5.1增加 5.1-2 map作为参数 5.2删除 5.3修改 5.4拓展 通过非主键删除 返回值是影响的行数 拓展2 通过非主键修改

服务器数据库被360后缀勒索病毒攻击怎么解决,勒索病毒解密

随着网络攻击日益猖獗&#xff0c;数据库遭遇勒索病毒的攻击已成为常见现象。而360后缀勒索病毒是一种恶意软件&#xff0c;它将加密数据库中的文件&#xff0c;并要求受害者支付赎金才能获得解密密钥。近日&#xff0c;我们收到很多企业的求助&#xff0c;企业的服务器被360后…

C语言小项目——通讯录高阶(文件管理版)

通讯录初阶: 点这里 通讯录中阶: 点这里 文件管理版本改进之处通讯录初始化退出通讯录并保存 完整代码contact.hcontact.ctest.c 文件管理版本改进之处 通讯录初始化 contact.c 退出通讯录并保存 test.c contact.c contact.h 完整代码 contact.h #pragma once#include&l…

2.8Menubar菜单

2.8Menubar菜单 这一次的效果将会像下面的图片一样. 注意这里的操作系统是苹果的 MacOS, 它的菜单栏位置和 Windows 的不一样. 下面那张图除了 Cut, Copy, Paste 的选项, 后面的都是 Apple 自己生成的选项, Windows 不会有的. menubar 部件 下面是我们制作整个菜单栏的流程…

纯CSS实现的卡片切换效果

纯CSS实现的卡片切换效果 无需JS就可以实现限于纯静态页面产品展示不需要轮播,自动切换 示例代码 <template><div class"example-css-tab"><div class"container dwo"><div class"card"><input type"radio"…

自动驾驶多任务框架 MultiTask V3、HybridNets和YOLOP比较

目标检测和分割是自动驾驶汽车感知系统的两个核心模块。它们应该具有高效率和低延迟,同时降低计算复杂性。目前,最常用的算法是基于深度神经网络的,这保证了高效率,但需要高性能的计算平台。 在自动驾驶汽车的场景下,大多使用的都是计算能力有限的嵌入式平台,这使得难以满…

Acwing.906 区间分组(贪心)

题目 给定N个闭区间[ai,bi]&#xff0c;请你将这些区间分成若千组&#xff0c;使得每组内部的区间两两之间(包括端点)没有交集&#xff0c;并使得组数尽可能小。 输出最小组数。 输入格式 第一行包含整数N&#xff0c;表示区间数。 接下来N行&#xff0c;每行包含两个整数ai…

uniapp离线引入阿里巴巴图标

阿里巴巴图标地址 1.添加图标到购物车 2.点击购物车进入项目 3.下载到本地 4.解压后文件目录 5.放入项目目录中(比如说我经常放在common或者static下icon中) 6.在main.ts或者main.js中引入&#xff08;注意路径&#xff0c;用相对的也行&#xff09; import /static/iconfon…

Redis进阶底层原理 - 高可用哨兵模式

Redis哨兵模式是redis实现高可用的一种分布式架构。哨兵节点是一种特殊的Redis实例&#xff0c;它不用于存储业务数据&#xff0c;它主要工作内容如下&#xff1a;&#xff08;高可用不是指不出问题&#xff0c;而是出了问题后能及时的自动化解决&#xff0c;从而在客户端无感&…