为了讲解本文的主题,我们需要先了解以下几个概念:
- 前后端分离:前后端分离是指将前端和后端业务逻辑分开,前端主要负责展示数据和交互逻辑,后端主要负责提供API接口和业务逻辑。
- Spring Security:Spring Security是基于Spring框架的安全框架,主要提供身份认证、授权、攻击防护等安全功能。
- Token认证:Token认证是一种基于Token的认证方式,服务器在认证成功后返回给客户端一个Token,客户端在请求接口时需要将Token带上,服务器根据Token来判断客户端的身份。
接下来,我们将讲解如何使用Spring Security实现前后端分离的示例。
一、前端实现Token认证
前端实现Token认证的目的是为了在用户成功登录后,获取Token并将其保存在本地,然后在访问需要认证的API接口时将Token带上。这里我们使用Vue.js作为前端框架,使用Axios作为http库,示例代码如下:
import axios from 'axios';
// 设置Token
function setToken(token) {
localStorage.setItem('Token', token);
}
// 获取Token
function getToken() {
return localStorage.getItem('Token');
}
// 在请求头中添加Token
axios.interceptors.request.use(function(config) {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 登录
export function login(username, password) {
return axios.post('/api/login', {
username,
password,
}).then(response => {
const token = response.data.token;
setToken(token);
return response;
});
}
如上代码中,我们使用localStorage
保存Token,在请求头中添加Token。当用户成功登录后,我们调用login
方法,将用户名和密码传给后端验证,验证成功后将Token保存在本地,并返回登录成功的响应结果。
二、后端实现Token认证
后端实现Token认证的主要目的是为了在客户端请求接口时,根据Token判断客户端的身份是否合法,如果合法则返回数据,如果不合法则返回401状态码。这里我们使用Spring Security提供的Token认证方式。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private SecurityProperties securityProperties;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 开启跨域
http.cors().and().csrf().disable();
// 登录过滤器
http.addFilterBefore(new JwtAuthenticationFilter(securityProperties), UsernamePasswordAuthenticationFilter.class);
// 其他请求都需要认证
http.authorizeRequests().anyRequest().authenticated();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
如上代码中,我们在SecurityConfig
类中使用@EnableWebSecurity
注解开启Spring Security,并将UserDetailsService
注入到configureGlobal
方法中,以便为用户提供身份认证功能。另外,我们还通过HttpSecurity
对象开启跨域、添加Token认证过滤器并设置其他的请求都需要认证。
具体的Token认证过滤器JwtAuthenticationFilter
实现如下:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final SecurityProperties securityProperties;
public JwtAuthenticationFilter(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = getTokenFromRequest(request);
if (StringUtils.hasText(token) && JwtUtils.validateToken(token, securityProperties.getSecret())) {
Authentication authentication = JwtUtils.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
SecurityContextHolder.getContext().setAuthentication(null);
}
chain.doFilter(request, response);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
如上代码中,我们从request中获取Token并校验,如果校验通过,则将认证信息存入SecurityContextHolder
中,如登录名、角色等信息。
三、示例一:请求用户信息
假设我们的系统中,需要获取当前登录用户的信息,我们可以在前端使用Axios在请求时将Token带上,如下:
function getUserInfo() {
return axios.get('/api/user/info').then(response => {
const userInfo = response.data.userInfo;
return userInfo;
});
}
如上代码中,我们调用getUserInfo
方法,Axios会自动将Token带上,并向后端请求用户信息。
然后,在后端我们需要编写一个接口获取当前登录用户的信息,示例代码如下:
@RestController
@RequestMapping("/api/user")
public class UserController {
@GetMapping("/info")
@PreAuthorize("hasRole('ADMIN')")
public Map<String, Object> getUserInfo(Authentication authentication) {
Map<String, Object> user = new HashMap<>();
if (authentication != null) {
user.put("username", authentication.getName());
user.put("roles", authentication.getAuthorities());
}
return Collections.singletonMap("userInfo", user);
}
}
如上代码中,我们使用@PreAuthorize("hasRole('ADMIN')")
注解来设置只有ADMIN角色的用户才可以访问该接口。当请求进来后,Spring Security会根据Token认证客户端的身份,并判断客户端是否具有ADMIN角色,最终根据判断结果返回用户信息或401状态码。
四、示例二:使用Token登录
接下来我们将讲解使用Token登录的示例。用户在登录时输入用户名和密码,后端根据用户名和密码验证成功返回Token,前端将Token保存在本地,在请求API时带上Token。我们在前端添加如下方法用于登录:
export function login(username, password) {
return axios.post('/api/login', {
username,
password,
}).then(response => {
const token = response.data.token;
setToken(token);
return response;
});
}
如上代码中,我们使用Axios向后端发送登录请求,如果成功则将返回的Token保存到本地。
接下来,我们需要在后端添加登录接口,示例代码如下:
@RestController
public class LoginController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtProperties jwtProperties;
@PostMapping("/api/login")
public Map<String, Object> login(@RequestBody LoginRequest request) {
final String username = request.getUsername();
final String password = request.getPassword();
Authentication authentication = null;
try {
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (BadCredentialsException e) {
throw new RuntimeException("用户名或密码错误");
}
String token = generateJwtToken(authentication);
return Collections.singletonMap("token", token);
}
private String generateJwtToken(Authentication authentication) {
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
return JwtUtils.generateToken(userDetails.getUsername(), jwtProperties.getSecret(), jwtProperties.getExpiration());
}
}
如上代码中,我们使用@PostMapping("/api/login")
注解来设置登录接口,当接收到前端的用户名和密码时,使用AuthenticationManager.authenticate
方法对用户名和密码进行验证,如果验证通过则生成一个Token,并将Token返回给前端。
至此,我们已经完成了使用Token实现登录的示例。
五、总结
本文详细讲解了使用Spring Security实现前后端分离的示例,涵盖了前端实现Token认证、后端实现Token认证以及两个示例的完整代码。在实际开发中,我们可以根据这个示例进行扩展,实现更加复杂的业务逻辑。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity实现前后端分离的示例详解 - Python技术站