Spring Boot + Spring Security + Vue实现前后端分离登录认证
本文参考地址:https://blog.csdn.net/I_am_Hutengfei/article/details/100561564/
上述地址中作者开发了后端的登录认证功能,但是对于普通的不涉及权限的前后端分离登录就略有不同,这里仅讲述与上述地址中作者描述不同的地方
LoginService.java
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import top.gerritchang.daily.menu.entity.UserEntity;
import top.gerritchang.daily.menu.mybatis.LoginMapper;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
@Service
public class LoginService implements UserDetailsService {
@Resource
private SqlSessionTemplate sqlSessionTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LoginMapper loginMapper = sqlSessionTemplate.getMapper(LoginMapper.class);
UserEntity userEntity = loginMapper.getUserByUName(username); //UserEntity是用户基本信息,后面调用的是自己的Mapper层查询用户的接口
if (userEntity == null) {
throw new UsernameNotFoundException("用户名不存在");
}
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //这个地方随便赋值一个角色就好了
userEntity.setAuthorities(authorities);
SecurityContextHolder.setContext(SecurityContextHolder.getContext());
return userEntity;
}
}
UserEntity.java
import java.util.Collection;
public class UserEntity implements UserDetails {
private String id;
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
CustomizeFilterInvocationSecurityMetadataSource.java
import java.util.Collection;
@Component
public class CustomizeFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
//获取请求地址
// String requestUrl = ((FilterInvocation) o).getRequestUrl();
// //查询具体某个接口的权限
// List<SysPermission> permissionList = sysPermissionService.selectListByPath(requestUrl);
// if(permissionList == null || permissionList.size() == 0){
// //请求路径没有配置权限,表明该请求接口可以任意访问
// return null;
// }
// String[] attributes = new String[permissionList.size()];
String[] attributes = new String[]{"ROLE_ADMIN"}; //这里和第一个文件的内容相同即可
// for(int i = 0;i<permissionList.size();i++){
// attributes[i] = permissionList.get(i).getPermissionCode();
// }
return SecurityConfig.createList(attributes);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
SecurityConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import top.gerritchang.daily.auth.*;
import top.gerritchang.daily.menu.service.LoginService;
import javax.annotation.Resource;
/**
* 这个是前后端不分离项目中登录
*/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource
private LoginService loginService;
@Resource
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Resource
private MyAuthenticationFailHandler myAuthenticationFailHandler;
@Resource
private CustomizeLogoutSuccessHandler logoutSuccessHandler;
@Resource
private CustomizeAuthenticationEntryPoint authenticationEntryPoint;
@Resource
private CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;
@Resource
private CustomizeAbstractSecurityInterceptor securityInterceptor;
//访问决策管理器
@Resource
CustomizeAccessDecisionManager accessDecisionManager;
//实现权限拦截
@Resource
CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(loginService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable();
httpSecurity
.cors()
.and()
.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(accessDecisionManager);//决策管理器
o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
return o;
}
})
.and().logout().logoutSuccessUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)//登出成功处理逻辑
.deleteCookies("JSESSIONID")
.and()
.formLogin()
.loginProcessingUrl("/login")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHandler)
.permitAll()
.and().exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and().sessionManagement()
.maximumSessions(1)//同一账号同时登录最大用户数
.expiredSessionStrategy(sessionInformationExpiredStrategy);
httpSecurity.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
其他在SecurityConfiguration里面用到的类与引用地址给出的一致即可,部分名字可能有出入,以引用地址为主即可
这时,调用axios登录的地址就变成了上述代码段里配置的"/login",那么axios去登录的话需要这样写:
let params = new FormData();
params.append("username", username);
params.append("password", password);
let config = {
headers: {"Content-Type": "application/x-www-form-urlencoded"}
};
let url = "http://localhost:8081/login";
axios.post(url, params, config)
这时,虽然可以正常登录了,但是呢,当你访问其他的资源的时候会报角色错,角色就变成了"ROLE_ANONYMOUS"。这时我们需要在前端里修改一下,让axios请求的时候带上cookies即可,具体代码如下:
import axios from 'axios' axios.defaults.withCredentials = true;

浙公网安备 33010602011771号