本文參考地址: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;
