赞
踩
在网上商城项目中,用户的安全性是非常重要的。为了实现用户权限认证和安全校验,我们可以使用Spring Security、JWT和Redis来实现。本篇博客将详细介绍后端和前台的实现过程,并提供相应的代码案例。
当用户点击登录按钮时,前端发送一个POST请求到后端的登录接口,传递用户名和密码。后端接收到请求后,验证用户名和密码的正确性。如果验证通过,后端生成一个JWT(JSON Web Token),并将其返回给前端。
前端接收到JWT后,将其存储在本地,例如使用localStorage。然后通过Vue Router进行页面跳转,跳转到需要进行权限校验的页面。
在需要进行权限校验的页面组件中,可以在created
钩子函数中检查本地存储中是否存在JWT。如果不存在,则表示用户未登录或登录已过期,可以通过Vue Router跳转到登录页面。
需要注意的是,在每次向后端发送请求时,需要将JWT作为请求头的Authorization字段进行传递,以便后端进行权限校验。
后端在接收到请求时,可以通过解析JWT来验证用户的身份和权限。JWT中通常包含用户ID、用户名、权限信息等。后端可以使用Spring Security的相关功能进行JWT的解析和校验。
总结来说,用户登录和权限认证的流程可以简化为以下几个步骤:
首先,在后端的Spring Boot项目中,我们需要配置Spring Security来实现用户权限认证。我们可以创建一个SecurityConfig
类来配置Spring Security。
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/public/**").permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .addFilter(new JwtAuthorizationFilter(authenticationManager(), userService)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
上述代码中,我们配置了允许访问/api/public/**
路径的请求,其他请求需要进行认证。我们还添加了两个过滤器JwtAuthenticationFilter
和JwtAuthorizationFilter
来处理JWT的认证和授权。
接下来,我们来实现JWT的认证和授权过滤器。
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; public JwtAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; setFilterProcessesUrl("/api/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { UserCredentials credentials = new ObjectMapper().readValue(request.getInputStream(), UserCredentials.class); return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( credentials.getUsername(), credentials.getPassword(), new ArrayList<>())); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { String token = Jwts.builder() .setSubject(((User) authResult.getPrincipal()).getUsername()) .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET) .compact(); response.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token); } }
public class JwtAuthorizationFilter extends BasicAuthenticationFilter { private UserService userService; public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserService userService) { super(authenticationManager); this.userService = userService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader(SecurityConstants.HEADER_STRING); if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) { chain.doFilter(request, response); return; } UsernamePasswordAuthenticationToken authentication = getAuthentication(request); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); } private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { String token = request.getHeader(SecurityConstants.HEADER_STRING); if (token != null) { String username = Jwts.parser() .setSigningKey(SecurityConstants.SECRET) .parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, "")) .getBody() .getSubject(); if (username != null) { User user = userService.loadUserByUsername(username); return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); } return null; } return null; } }
上述代码中,JwtAuthenticationFilter
处理登录请求,将用户的认证信息封装为JWT,并添加到响应头中。JwtAuthorizationFilter
处理其他请求,从请求头中获取JWT并进行认证和授权。
为了实现用户的认证和授权,我们需要创建一个用户服务实现类UserService
。
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
上述代码中,我们通过用户名从数据库中获取用户信息。
为了增加用户登录的安全性,我们可以将JWT存储到Redis中,并设置过期时间。
@Service public class TokenService { private RedisTemplate<String, Object> redisTemplate; public TokenService(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } public void saveToken(String username, String token) { redisTemplate.opsForValue().set(username, token, SecurityConstants.EXPIRATION_TIME, TimeUnit.MILLISECONDS); } public boolean validateToken(String username, String token) { String storedToken = (String) redisTemplate.opsForValue().get(username); return storedToken != null && storedToken.equals(token); } public void deleteToken(String username) { redisTemplate.delete(username); } }
上述代码中,我们使用redisTemplate
将用户名和JWT存储到Redis中,并设置过期时间。在校验Token时,我们从Redis中获取存储的Token进行比较。
在前台,我们使用Vue来实现用户登录和权限认证的功能。我们需要创建相应的组件和路由来实现用户登录和权限校验。
<template> <div> <h1>用户登录</h1> <form @submit.prevent="login"> <label for="username">用户名:</label> <input type="text" id="username" v-model="username" required> <br> <label for="password">密码:</label> <input type="password" id="password" v-model="password" required> <br> <button type="submit">登录</button> </form> </div> </template> <script> export default { data() { return { username: '', password: '' }; }, methods: { login() { // 调用后端API接口进行用户登录 // 使用axios或其他HTTP库发送POST请求 } } }; </script>
import Vue from 'vue'; import VueRouter from 'vue-router'; import Login from './components/Login.vue'; import ProductList from './components/ProductList.vue'; Vue.use(VueRouter); const routes = [ { path: '/login', component: Login }, { path: '/products', component: ProductList, meta: { requiresAuth: true } } ]; const router = new VueRouter({ routes }); router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.matched.some(record => record.meta.requiresAuth)) { if (!token) { next('/login'); } else { next(); } } else { next(); } }); export default router;
在上述代码中,我们配置了两个路由,/login
用于用户登录,/products
用于展示商品列表。在/products
路由中,我们设置了requiresAuth
为true
,表示需要进行权限校验。在路由导航守卫中,我们检查是否存在Token,如果不存在则跳转到登录页面。
import Vue from 'vue';
import App from './App.vue';
import router from './router';
new Vue({el: '#app',
router,
render: h => h(App)
}).$mount('#app');
在入口文件中,我们创建了一个Vue实例,并将路由配置和根组件传入。然后将Vue实例挂载到#app
元素上。
在登录组件中,我们需要调用后端API接口进行用户登录,并将返回的Token保存到本地存储中。
methods: { login() { axios.post('/api/login', { username: this.username, password: this.password }) .then(response => { const token = response.data.token; localStorage.setItem('token', token); this.$router.push('/products'); }) .catch(error => { console.error(error); }); } }
在上述代码中,我们使用axios库发送POST请求到后端的登录接口。如果登录成功,会返回一个包含Token的响应。我们将Token保存到本地存储中,并使用$router.push
方法跳转到商品列表页面。
在商品列表组件中,我们需要进行权限校验,只有在用户登录成功并且存在Token的情况下才能访问该页面。
export default {
created() {
if (!localStorage.getItem('token')) {
this.$router.push('/login');
}
}
};
在上述代码中,我们在组件创建时检查本地存储中是否存在Token,如果不存在则跳转到登录页面。
本文介绍了如何使用Spring Security和Vue实现用户登录和权限认证的功能。通过使用JWT和Redis存储Token,我们可以实现无状态的用户认证,提高系统的可扩展性和安全性。同时,通过使用Vue的路由导航守卫,我们可以实现前端的权限校验,确保只有登录用户才能访问受限资源。希望本文能对你理解和实现用户登录和权限认证功能有所帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。