简介:springboot框架整合security与jwt实现登录,访问数据鉴权,jwt登出,接口角色控制,接口权限控制
技术:springboot,redis,security,jwt
核心代码:
pom
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.0</version> </dependency>
WebSecurityConfig 核心配置类
主要用于:
指定密码加密方式:BCryptPasswordEncoder
指定登录鉴权过滤器:JwtLoginFilter
指定接口访问鉴权过滤器:JwtFilter
指定查询用户信息的自定义service:UserService
package com.auth.auth.config; import com.auth.auth.constant.JWTConstants; import com.auth.auth.filter.JwtFilter; import com.auth.auth.filter.JwtLoginFilter; import com.auth.auth.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated().and() .addFilterBefore(new JwtLoginFilter(JWTConstants.JWT_LOGIN_URL, authenticationManager()), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class) .csrf() .disable(); } }
登录鉴权类
鉴权:这里获取用户名与密码调用指定api即可
鉴权成功:这里需要基于jwt生成token,响应给调用者
鉴权失败:返回错误信息即可
package com.auth.auth.filter; import com.auth.auth.constant.SystemConstants; import com.auth.auth.entities.User; import com.auth.auth.util.JwtUtils; import com.auth.auth.util.ResponseUtil; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter { public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) { super(new AntPathRequestMatcher(defaultFilterProcessesUrl)); setAuthenticationManager(authenticationManager); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse resp) throws AuthenticationException { String username = request.getParameter(SystemConstants.LOGIN_INPUT_PARAM_USERNAME); String password = request.getParameter(SystemConstants.LOGIN_INPUT_PARAM_PASSWORD); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); return this.getAuthenticationManager().authenticate(authRequest); } @Override protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse resp, FilterChain chain, Authentication authentication) { User userDetails = (User) authentication.getPrincipal(); String jwt = JwtUtils.getInstance().generateToken(userDetails.getUsername(), userDetails.getRole(), userDetails.getPermissions()); ResponseUtil.success(resp, jwt); } @Override protected void unsuccessfulAuthentication(HttpServletRequest req, HttpServletResponse resp, AuthenticationException failed) { ResponseUtil.fail(resp); } }
接口访问鉴权
拦截不在白名单的所有接口
获取请求头里的jwtToken并校验其是否可用
package com.auth.auth.filter; import com.auth.auth.constant.AuthExceptionMessage; import com.auth.auth.constant.JWTConstants; import com.auth.auth.util.JwtUtils; import com.auth.auth.util.ResponseUtil; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class JwtFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; String jwtToken = req.getHeader(JWTConstants.JWT_REQUEST_HEADER_KEY); try { UserDetails user = JwtUtils.getInstance().decode(jwtToken); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(token); } catch (Exception e) { e.printStackTrace(); ResponseUtil.responseJson(servletResponse, AuthExceptionMessage.INVALID_TOKEN); } filterChain.doFilter(req, servletResponse); } }
自定义用户信息查询service
package com.auth.auth.service; import org.springframework.security.core.userdetails.UserDetailsService; public interface UserService extends UserDetailsService { }
package com.auth.auth.service.impl; import com.auth.auth.entities.User; import com.auth.auth.service.UserService; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { User user = new User(); user.setUsername(userName); user.setPassword("$2a$10$Zr6M2iK41CbW0wKLweuilu7PGaCgCF24TD16w7IvTiNkcRE1ZlxM.");//123456 user.setId("1"); user.setRole("dev"); String permissions = "auth:test:test1,ROLE_" + user.getRole(); user.setPermissions(permissions); return user; } }
jwttoken生成与校验工具类
package com.auth.auth.util; import com.auth.auth.constant.JWTConstants; import com.auth.auth.entities.User; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import org.joda.time.DateTime; import org.springframework.stereotype.Component; import java.util.Map; @Component public class JwtUtils { private static final JwtUtils jwtUtils = new JwtUtils(); private JwtUtils() { } public static JwtUtils getInstance() { return jwtUtils; } Algorithm getAlgorithm() { return Algorithm.HMAC256(JWTConstants.JWT_KEY); } public String generateToken(String userName, String userRole, String userPermission) { return JWT.create() .withClaim(JWTConstants.JWT_KEY_USER_NAME, userName) .withClaim(JWTConstants.JWT_KEY_ROLE, userRole) .withClaim(JWTConstants.JWT_KEY_PERMISSION, userPermission) .withExpiresAt(DateTime.now().plusDays(JWTConstants.EXPIRE_DAY).toDate()) .sign(getAlgorithm()); } public User decode(String token) { DecodedJWT decodedJWT = JWT.require(getAlgorithm()).build().verify(token); Map<String, Claim> jwt = decodedJWT.getClaims(); String userName = jwt.get(JWTConstants.JWT_KEY_USER_NAME).asString(); String userRole = jwt.get(JWTConstants.JWT_KEY_ROLE).asString(); String userPermission = jwt.get(JWTConstants.JWT_KEY_PERMISSION).asString(); return new User(userName, userRole, userPermission); } }
编写测试接口
使用权限注解需要加注解@EnableGlobalMethodSecurity(prePostEnabled = true)(我加在了WebSecurityConfig类上了)
package com.auth.auth.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class TestController { @PreAuthorize("hasPermission('auth','auth:test:test1')") @RequestMapping("test1") public String test() { return "test1::success"; } @PreAuthorize("hasRole('dev')") @RequestMapping("test2") public String test3() { return "test2::success"; } @RequestMapping("test3") public String test4() { return "test3::success"; } }
hasPermission注解也需要自定义处理逻辑
package com.auth.auth.util; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; import java.io.Serializable; import java.util.Set; import java.util.stream.Collectors; @Component public class CustomPermissionEvaluator implements PermissionEvaluator { @Override public boolean hasPermission(Authentication authentication, Object accessType, Object permission) { if (authentication != null && authentication.getAuthorities() != null) { Set<String> permissionSet = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); if (accessType instanceof String) { return permissionSet.contains(permission); } } return false; } @Override public boolean hasPermission(Authentication authentication, Serializable serializable, String targetType, Object permission) { return hasPermission(authentication, targetType, permission); } }
测试
登录:
访问数据接口
访问限制角色的数据接口
访问限制权限的数据接口
本文章只展示核心代码,完整代码见git,文首有git地址
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)