Spring Boot + Vue的網(wǎng)上商城之springsecurity+jwt+redis實現(xiàn)用戶權(quán)限認證實現(xiàn)
在網(wǎng)上商城項目中,用戶的安全性是非常重要的。為了實現(xiàn)用戶權(quán)限認證和安全校驗,我們可以使用Spring Security、JWT和Redis來實現(xiàn)。本篇博客將詳細介紹后端和前臺的實現(xiàn)過程,并提供相應(yīng)的代碼案例。
思路
當(dāng)用戶點擊登錄按鈕時,前端發(fā)送一個POST請求到后端的登錄接口,傳遞用戶名和密碼。后端接收到請求后,驗證用戶名和密碼的正確性。如果驗證通過,后端生成一個JWT(JSON Web Token),并將其返回給前端。
前端接收到JWT后,將其存儲在本地,例如使用localStorage。然后通過Vue Router進行頁面跳轉(zhuǎn),跳轉(zhuǎn)到需要進行權(quán)限校驗的頁面。
在需要進行權(quán)限校驗的頁面組件中,可以在created
鉤子函數(shù)中檢查本地存儲中是否存在JWT。如果不存在,則表示用戶未登錄或登錄已過期,可以通過Vue Router跳轉(zhuǎn)到登錄頁面。
需要注意的是,在每次向后端發(fā)送請求時,需要將JWT作為請求頭的Authorization字段進行傳遞,以便后端進行權(quán)限校驗。
后端在接收到請求時,可以通過解析JWT來驗證用戶的身份和權(quán)限。JWT中通常包含用戶ID、用戶名、權(quán)限信息等。后端可以使用Spring Security的相關(guān)功能進行JWT的解析和校驗。
總結(jié)來說,用戶登錄和權(quán)限認證的流程可以簡化為以下幾個步驟:
- 用戶在前端頁面輸入用戶名和密碼,并點擊登錄按鈕。
- 前端發(fā)送POST請求到后端的登錄接口,傳遞用戶名和密碼。
- 后端驗證用戶名和密碼的正確性,如果驗證通過,生成JWT并返回給前端。
- 前端接收到JWT后,將其存儲在本地。
- 前端通過Vue Router進行頁面跳轉(zhuǎn),跳轉(zhuǎn)到需要進行權(quán)限校驗的頁面。
- 在需要進行權(quán)限校驗的頁面組件中,檢查本地存儲中是否存在JWT,如果不存在,則跳轉(zhuǎn)到登錄頁面。
- 在每次向后端發(fā)送請求時,將JWT作為請求頭的Authorization字段進行傳遞。
- 后端在接收到請求時,解析JWT并進行權(quán)限校驗,校驗通過則返回相應(yīng)的數(shù)據(jù)或頁面。
后端實現(xiàn)
Spring Security配置
首先,在后端的Spring Boot項目中,我們需要配置Spring Security來實現(xiàn)用戶權(quán)限認證。我們可以創(chuàng)建一個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的認證和授權(quán)。
JWT認證和授權(quán)過濾器
接下來,我們來實現(xiàn)JWT的認證和授權(quán)過濾器。
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,并添加到響應(yīng)頭中。JwtAuthorizationFilter
處理其他請求,從請求頭中獲取JWT并進行認證和授權(quán)。
用戶服務(wù)和認證
為了實現(xiàn)用戶的認證和授權(quán),我們需要創(chuàng)建一個用戶服務(wù)實現(xiàn)類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;
}
}
上述代碼中,我們通過用戶名從數(shù)據(jù)庫中獲取用戶信息。
Redis存儲Token
為了增加用戶登錄的安全性,我們可以將JWT存儲到Redis中,并設(shè)置過期時間。
@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中,并設(shè)置過期時間。在校驗Token時,我們從Redis中獲取存儲的Token進行比較。
前臺實現(xiàn)
在前臺,我們使用Vue來實現(xiàn)用戶登錄和權(quán)限認證的功能。我們需要創(chuàng)建相應(yīng)的組件和路由來實現(xiàn)用戶登錄和權(quán)限校驗。
登錄組件
<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() {
// 調(diào)用后端API接口進行用戶登錄
// 使用axios或其他HTTP庫發(fā)送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
路由中,我們設(shè)置了requiresAuth
為true
,表示需要進行權(quán)限校驗。在路由導(dǎo)航守衛(wèi)中,我們檢查是否存在Token,如果不存在則跳轉(zhuǎn)到登錄頁面。
入口文件
import Vue from 'vue';
import App from './App.vue';
import router from './router';
new Vue({el: '#app',
router,
render: h => h(App)
}).$mount('#app');
在入口文件中,我們創(chuàng)建了一個Vue實例,并將路由配置和根組件傳入。然后將Vue實例掛載到#app
元素上。
用戶登錄
在登錄組件中,我們需要調(diào)用后端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庫發(fā)送POST請求到后端的登錄接口。如果登錄成功,會返回一個包含Token的響應(yīng)。我們將Token保存到本地存儲中,并使用$router.push
方法跳轉(zhuǎn)到商品列表頁面。
權(quán)限校驗
在商品列表組件中,我們需要進行權(quán)限校驗,只有在用戶登錄成功并且存在Token的情況下才能訪問該頁面。
export default {
created() {
if (!localStorage.getItem('token')) {
this.$router.push('/login');
}
}
};
在上述代碼中,我們在組件創(chuàng)建時檢查本地存儲中是否存在Token,如果不存在則跳轉(zhuǎn)到登錄頁面。文章來源:http://www.zghlxwxcb.cn/news/detail-733538.html
總結(jié)
本文介紹了如何使用Spring Security和Vue實現(xiàn)用戶登錄和權(quán)限認證的功能。通過使用JWT和Redis存儲Token,我們可以實現(xiàn)無狀態(tài)的用戶認證,提高系統(tǒng)的可擴展性和安全性。同時,通過使用Vue的路由導(dǎo)航守衛(wèi),我們可以實現(xiàn)前端的權(quán)限校驗,確保只有登錄用戶才能訪問受限資源。希望本文能對你理解和實現(xiàn)用戶登錄和權(quán)限認證功能有所幫助。文章來源地址http://www.zghlxwxcb.cn/news/detail-733538.html
到了這里,關(guān)于Spring Boot + Vue的網(wǎng)上商城之springsecurity+jwt+redis實現(xiàn)用戶權(quán)限認證實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!