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