前言
最近把spring-cloud的注册中心改用了Nacos,顺便整合了一下spring-security,前端采用vue实现登录,废话不多说,开始了。
一、后端
1、nacos集成
a. 下载nacos。我是用的2.0版本以上
b. 解压,启动sh startup.sh -m standalone
c. http://localhost:8848/nacos/,账号nacos/nacos
d.pom依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、创建gateway集成security
SecurityConfig配置
SecurityWebFilterChain chain = http.formLogin()
.loginPage("/login")
// 登录成功handler
.authenticationSuccessHandler(jsonServerAuthenticationSuccessHandler)
// 登陆失败handler
.authenticationFailureHandler(jsonServerAuthenticationFailureHandler)
// 无访问权限handler
.authenticationEntryPoint(serverAuthenticationEntryPoint)
.and()
.logout()
// 登出成功handler
.logoutSuccessHandler(jsonServerLogoutSuccessHandler)
.and()
.csrf().disable()
.httpBasic().disable()
.authorizeExchange()
// 白名单放行
.pathMatchers(AUTH_WHITELIST).permitAll()
// 访问权限控制
.anyExchange().access(authorizeConfigManager).and().build();
@Bean
ReactiveAuthenticationManager reactiveAuthenticationManager() {
LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
managers.add(authenticationManager);
return new DelegatingReactiveAuthenticationManager(managers);
}
Api请求校验类
AuthorizeConfigManager
@Slf4j
@Component
public class AuthorizeConfigManager implements ReactiveAuthorizationManager<AuthorizationContext> {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication,
AuthorizationContext authorizationContext) {
boolean check = false;
ServerWebExchange exchange = authorizationContext.getExchange();
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
Authentication authentication1=JwtTokenUtils.getAuthenticationeFromToken(request);
if (null != authentication1) {
for (GrantedAuthority authority : authentication1.getAuthorities()) {
if (antPathMatcher.match(authority.getAuthority(), path)) {
log.info(String.format("用户请求API校验通过,GrantedAuthority:{%s} Path:{%s} ", authority.getAuthority(), path));
check = true;
}
}
}
return Mono.just(new AuthorizationDecision(check));
}
@Override
public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
return check(authentication, object)
.filter(d -> d.isGranted())
.switchIfEmpty(Mono.defer(() -> {
String body = ErrorCode.SYSTEM_ERROR.toString();
return Mono.error(new AccessDeniedException(body));
}))
.flatMap(d -> Mono.empty());
}
}
登录成功类
JsonServerAuthenticationSuccessHandler
@Component
public class JsonServerAuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
@Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
// 登录成功后可以放入一些参数到session中
ServerHttpResponse response = webFilterExchange.getExchange().getResponse();
response.setStatusCode(HttpStatus.OK);
// 系统登录认证
String token = JwtTokenUtils.generateToken(authentication);
response.getHeaders().set("token",token);
response.getHeaders().set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");
DataBuffer buffer = response.bufferFactory().wrap(
ResultDataUtils.getResponse(ErrorCode.SUCCESS.getCode(),ErrorCode.SUCCESS.getMessage(),authentication.getAuthorities()).getBytes(CharsetUtil.UTF_8));
return response.writeWith(Mono.just(buffer));
}
}
获取用户信息类
public class UserInfoUtil {
public static final String SECRET = "xxx";
//用于子服务获取用户信息
public static String getUserName() throws GlobalException {
return getUserName(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("token"));
}
//用于gateway获取用户信息
public static String getUserName(String token) throws GlobalException {
if (token != null) {
if (isTokenExpired(token)) {
throw new GlobalException(ErrorCode.EXPIRED);
}
Claims claims = getClaimsFromToken(token);
if (claims == null) {
return null;
}
String username = claims.getSubject();
if (username == null) {
return null;
}
return username;
}
return null;
}
public static Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return true;
}
}
public static Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
}
二、前端
调用配置
export const login = data => axiosRequest.formdata('/login', data)
export function formdata(url, params = {}) {
return new Promise((resolve, reject) => {
httpService({
url: url,
method: 'post',
data: params,
transformRequest: [
function (data) {
let ret = ''
for (let 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; charset=UTF-8'}
}).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
关键代码就是上面这些,还有其他的代码,下回有空再补吧。有需要的童鞋也可以一起讨论。~~其实是有点懒~~不想贴了.....