后端:
1.引入maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
2.配置代码
package com.breeze.bms.config;
import com.breeze.bms.bean.MessageCode;
import com.breeze.bms.bean.Result;
import com.breeze.bms.bean.vo.LoginRes;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsUtils;
import javax.annotation.Resource;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @author breeze
* @since 2021/9/17 9:55
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
ObjectMapper objectMapper;
@Qualifier("customUserDetailService")
@Autowired
UserDetailsService userDetailsService;
@Override
public void configure(WebSecurity web) throws Exception {
//配置免登陆接口
web.ignoring().antMatchers("/doc.html", "/webjars/**", "/swagger-resources/**", "/v2/api-docs", "/anonymous/**","/stomp/websocketJS/**");
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().invalidSessionStrategy((request, response) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(Result.failResult(MessageCode.NO_LOGIN)));
out.flush();
out.close();
}).and().cors()
.and().csrf().disable()
.authenticationProvider(authenticationProvider())
.httpBasic()
//未登录时,进行json格式的提示
.authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(Result.failResult(MessageCode.NO_LOGIN)));
out.flush();
out.close();
})
.and()
.authorizeRequests()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
.anyRequest().authenticated() //必须授权才能范围
.and()
.formLogin().usernameParameter("username").passwordParameter("password").loginPage("/login").loginProcessingUrl("/login")
.permitAll()
//登录失败,返回json
.failureHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = Result.failResult("401", "登录失败");
if (ex instanceof UsernameNotFoundException) {
result.setMessage("未找到相关账户");
} else if (ex instanceof BadCredentialsException) {
result.setMessage("账户密码错误");
} else if (ex instanceof DisabledException) {
result.setMessage("账户被禁用");
}
out.write(objectMapper.writeValueAsString(result));
out.flush();
out.close();
})
//登录成功,返回json
.successHandler((request, response, authentication) -> {
Result result = Result.successResult();
result.setData(LoginRes.builder().token(request.getSession().getId()).authentication(authentication).build());
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(result));
out.flush();
out.close();
})
.and()
.exceptionHandling()
//没有权限,返回json
.accessDeniedHandler((request, response, ex) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = Result.failResult(MessageCode.NO_LOGIN, "权限不足");
out.write(objectMapper.writeValueAsString(result));
out.flush();
out.close();
})
.and()
.logout()
//退出成功,返回json
.logoutSuccessHandler((request, response, authentication) -> {
Map<String, Object> map = new HashMap<String, Object>();
Result result = Result.successResult();
result.setData(authentication);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(result));
out.flush();
out.close();
})
.permitAll();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
//对默认的UserDetailsService进行覆盖
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
3.获取用户的UserDetail实现
package com.breeze.bms.security;
import com.breeze.bms.mybatis.domain.SysUser;
import com.breeze.bms.service.SysUserService;
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 javax.annotation.Resource;
/**
* @author breeze
* @since 2021/9/17 11:03
*/
@Service("customUserDetailService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
SysUserService sysUserService; //自己的用户管理服务
@Override
public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.queryByLoginName(loginName);
return sysUser;
}
}
4.设置SpringSession头,如果不想自定义默认就行
package com.breeze.bms.config;
import org.springframework.context.annotation.Bean;
import org.springframework.session.MapSession;
import org.springframework.session.MapSessionRepository;
import org.springframework.session.SessionRepository;
import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession;
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
import org.springframework.session.web.http.HttpSessionIdResolver;
import java.util.concurrent.ConcurrentHashMap;
@EnableSpringHttpSession
public class SpringSessionConfig {
@Bean
public SessionRepository<MapSession> sessionRepository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
@Bean
public HttpSessionIdResolver sessionIdResolver() {
return new HeaderHttpSessionIdResolver("X-Token");
}
}
前端:
request({
url: '/login',
method: 'post',
data,
transformRequest: [
function(data) {
let ret = ''
for (const it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
ret = ret.substring(0, ret.lastIndexOf('&'))
return ret
}
],
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
其他访问授权接口的时候把登录返回的token放到请求头X-Token(此处为后端4.Spring Session里自定义的头)里就行,如下所示
service.interceptors.request.use(
config => {
// do something before request is sent
if (store.getters.token) {
// let each request carry token
// ['X-Token'] is a custom headers key
// please modify it according to the actual situation
config.headers['X-Token'] = JSON.parse(getToken()).token
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)