
Spring Security+JWT+Vue实现一个前后端分离无状态认证Demo
发布日期:2021-05-19 16:35:23
浏览次数:23
分类:精选文章
本文共 8898 字,大约阅读时间需要 29 分钟。
Spring Security + JWT 实现认证系统
本文将详细介绍如何使用 Spring Security 结合 JWT 实现认证系统的搭建,包含后端 API 接口开发、前端实现以及相关技术要点分享。
后端架构
1. 依赖管理
- Spring Boot: 用于快速构建后端服务。
- Spring Security: 提供安全认证功能。
- JWT: 用于令牌生成与验证。
- JAX-BIND: 用于imeline JSON 序列化。
此外,为确保兼容性,需在 Java 11 启用以下依赖:
com.sun.xml.bind jaxb-api 2.3.0
2. User 接口定义
将用户信息扩展 LinkedHashMap 实现 UserDetails 接口:
public class User implements UserDetails { private String username; private String password; private Boolean rememberMe; private String verifyCode; private String power; private Long expirationTime; private Listauthorities; @Override public Collection getAuthorities() { return authorities; } // 省略 get/set 方法...}
3. JWT 认证工具
TokenAuthenticationHelper 负责令牌验证与生成:
public class TokenAuthenticationHelper { private static final long EXPIRATION_TIME = 7200000; private static final int COOKIE_EXPIRATION_TIME = 1296000; private static final String SECRET_KEY = "ThisIsASpringSecurityDemo"; public static void addAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { // 生成并写入令牌 String jwt = Jwts.builder() .setSubject(authResult.getName()) .claim("authorities", new StringBuffer()) .forEach(authority -> authorBuffer.append(authority.getAuthority() + ",") ) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET_KEY) .compact(); Cookie cookie = new Cookie("COOKIE-TOKEN", jwt); cookie.setHttpOnly(true).setPath("/").setMaxAge(COOKIE_EXPIRATION_TIME); response.addCookie(cookie); // 向前端返回详细信息 LoginResultDetails loginResultDetails = new LoginResultDetails(); ResultDetails resultDetails = new ResultDetails(); resultDetails.setStatus(200).setSuccess(true) .setTimestamp(new LocalDateTime()) .setMessage("登录成功!"); User user = new User(); user.setUsername(authResult.getName()); user.setPower(authorities.toString()); user.setExpirationTime(System.currentTimeMillis() + EXPIRATION_TIME); loginResultDetails.setResultDetails(resultDetails) .setStatus(200) .setUser(user); response.setContentType("application/json"); ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getWriter(), loginResultDetails); } // 定义其他方法(如 getAuthentication...)}
JWT 过滤器配置
1. 登录过滤器(JwtLoginFilter)
负责处理 HTTP 请求的登录逻辑:
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter { private LoginCountService loginCountService; private VerifyCodeService verifyCodeService; public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager, VerifyCodeService verifyCodeService, LoginCountService loginCountService) { super(new AntPathRequestMatcher(defaultFilterProcessesUrl)); this.loginCountService = loginCountService; setAuthenticationManager(authenticationManager); this.verifyCodeService = verifyCodeService; } @Override protected Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException { loginCountService.judgeLoginCount(request); User user = new ObjectMapper().readValue(request.getInputStream(), User.class); verifyCodeService.verify(request.getSession().getId(), user.getVerifyCode()); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( user.getUsername(), user.getPassword(), user.getAuthorities() ); token.setDetails(new LoginDetails(user.getRememberMe(), user.getVerifyCode())); return getAuthenticationManager().authenticate(token); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException { loginCountService.cleanLoginCount(request); TokenAuthenticationHelper.addAuthentication(request, response, authResult); } // 定义其他方法(如 unsuccessfulAuthentication...)}
2. 遍历过滤器(JwtAuthenticationFilter)
负责处理每个请求,验证令牌有效性:
public class JwtAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException { try { Authentication authentication = TokenAuthenticationHelper.getAuthentication(request); SecurityContextHolder.getContext().setAuthentication(authentication); filterChain.doFilter(request, response); } catch (ExpiredJwtException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token expired, login expired"); } }}
Spring Security 配置
1. 安全配置(WebSecurityConfig)
@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { public static final String ADMIN = "ROLE_ADMIN"; public static final String USER = "ROLE_USER"; private VerifyCodeService verifyCodeService; private LoginCountService loginCountService; public WebSecurityConfig(VerifyCodeService verifyCodeService, LoginCountService loginCountService) { this.verifyCodeService = verifyCodeService; this.loginCountService = loginCountService; } // 全局 CORS 配置 @Bean public CorsConfigurationSource corsConfigurationSource() { ListallowedOriginsUrl = new ArrayList<>(); allowedOriginsUrl.add("http://localhost:8080"); allowedOriginsUrl.add("http://127.0.0.1:8080"); CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowCredentials(true); configuration.setAllowedOrigins(allowedOriginsUrl); configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(permitAllMappings).permitAll() .antMatchers("/api/user/**", "/api/data", "/api/logout").permitAll() .hasAnyAuthority(USER, ADMIN).antMatchers("/api/admin/**").hasAuthority(ADMIN) .anyRequest().authenticated().and().csrf().tokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); http.addFilterBefore(JwtLoginFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(JwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); http.securityFilterChain.addFilterAfter(new CsrfFilter(), UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(WebSecurity web) throws Exception { super.configure(web); } // 在内存中配置用户 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(daoAuthenticationProvider()); } @Bean public DaoAuthenticationProvider daoAuthenticationProvider() { return new DaoAuthenticationProvider(); }}
前端实现
1. Vue 项目搭建
基于 Vue CLI 创建项目,并配置如下:
// vue.config.jsimport Vue from 'vue'import ElementUI from 'element-ui'import mavonEditor from 'mavon-editor'import VueCookies from 'vue-cookies'import axios from 'axios'Vue.use(ElementUI)Vue.use(mavonEditor)Vue.use(VueCookies)Vue.config.productionTip = false// 配置后端 API 地址Vue.prototype.SERVER_API_URL = 'http://127.0.0.1:8088/api'new Vue({ router, store, render: h => h(App)}).$mount('#app')
2. 引入 Element UI 组件
在主应用文件中添加配置:
// main.jsimport axios from 'axios'axios.defaults.withCredentials = trueaxios.defaults.headers.common['X-CSRF-TOKEN'] = (this.$cookies.get('XSRF-TOKEN') || '')Vue.prototype.$axios = axiosVue.use(VueCookies)
3. 跨域配置
在 vue.config.js 中设置代理:
module.exports = { devServer: { proxy: { '/api': { target: 'http://127.0.0.1:8088', changeOrigin: true, ws: true, pathRewrite: { '^/api': '' } } } }}
注意事项
请求头配置:AJAX 请求中需携带 X-CSRF-TOKEN
和 Credentials: "include"
,否则可能导致跨域攻击。
令牌验证:确保前端请求中携带有效的令牌信息,后端需正确解析令牌并验证权限。
权限管理:根据用户角色设置权限,确保资源访问安全。
以上内容形成了一个完整的 Spring Security + JWT 认证系统实现方案,涵盖后端 API 开发、前端配置以及相关技术关键点。如有疑问或建议,欢迎在评论区留言交流!
发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2025年05月07日 02时04分51秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
使用 TortoiseGit 时,报 Access denied 错误
2021-05-09
c++之程序流程控制
2021-05-09
《机器学习Python实现_10_06_集成学习_boosting_gbdt分类实现》
2021-05-09
看明白这两种情况,才敢说自己懂跨链! | 喵懂区块链24期
2021-05-10
Android DEX加固方案与原理
2021-05-10
iOS_Runtime3_动态添加方法
2021-05-10
我用wxPython搭建GUI量化系统之最小架构的运行
2021-05-10
selenium+python之切换窗口
2021-05-10
map[]和map.at()取值之间的区别
2021-05-11
VTK:可视化之RandomProbe
2021-05-12
Java时间
2021-05-12
【编程】C语言入门:1到 100 的所有整数中出现多少个数字9
2021-05-12
pair的用法
2021-05-12
javaWeb服务详解(含源代码,测试通过,注释) ——Emp的Dao层
2021-05-14
echarts 基本图表开发小结
2021-05-14
TreeSet、TreeMap
2021-05-14
JVM内存模型
2021-05-14
可变长度参数
2021-05-14
GitHub上传时,项目在已有文档时直接push出现错误解决方案
2021-05-14