登录验证在security中确定方法,在admin和api中确定实现,不在使用url中的grant_type来确定

This commit is contained in:
hanfeng
2019-08-21 16:50:37 +08:00
parent 090e0cd867
commit 24ac928fb9
9 changed files with 79 additions and 48 deletions

View File

@@ -14,14 +14,14 @@ package com.yami.shop.security.config;
import com.yami.shop.security.filter.LoginAuthenticationFilter;
import com.yami.shop.security.handler.LoginAuthFailedHandler;
import com.yami.shop.security.handler.LoginAuthSuccessHandler;
import com.yami.shop.security.provider.AdminAuthenticationProvider;
import com.yami.shop.security.provider.MiniAppAuthenticationProvider;
import com.yami.shop.security.provider.AuthenticationTokenParser;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
@@ -39,10 +39,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private LoginAuthFailedHandler loginAuthFailedHandler;
@Autowired
private AdminAuthenticationProvider adminAuthenticationProvider;
private AuthenticationProvider authenticationProvider;
@Autowired
private MiniAppAuthenticationProvider miniAppAuthenticationProvider;
private AuthenticationTokenParser authenticationTokenParser;
@Override
@Bean
@@ -57,8 +57,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
*/
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(adminAuthenticationProvider);
auth.authenticationProvider(miniAppAuthenticationProvider);
auth.authenticationProvider(authenticationProvider);
}
@Bean
@@ -77,6 +76,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
filter.setAuthenticationSuccessHandler(loginAuthSuccessHandler);
filter.setAuthenticationFailureHandler(loginAuthFailedHandler);
filter.setAuthenticationTokenParser(authenticationTokenParser);
return filter;
}

View File

@@ -12,16 +12,11 @@ package com.yami.shop.security.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.yami.shop.common.util.Json;
import com.yami.shop.security.constants.SecurityConstants;
import com.yami.shop.security.exception.UnknownGrantTypeExceptionBase;
import com.yami.shop.security.token.AdminAuthenticationToken;
import com.yami.shop.security.token.MiniAppAuthenticationToken;
import com.yami.shop.security.provider.AuthenticationTokenParser;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.ServletInputStream;
@@ -32,44 +27,28 @@ import java.nio.charset.StandardCharsets;
/**
* 小程序登陆此时principal为code
* post:http://127.0.0.1:8086/login?grant_type=mini_app
* post:http://127.0.0.1:8086/login
* {principal:code}
* 管理员登陆:
* post: http://127.0.0.1:8086/login?grant_type=admin
* post: http://127.0.0.1:8086/login
* {principal:username,credentials:password}
*/
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationTokenParser authenticationTokenParser;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!ServletUtil.METHOD_POST.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String type = obtainParameter(request, OAuth2Utils.GRANT_TYPE);
AbstractAuthenticationToken authRequest = null;
String requestBody = getStringFromStream(request);
if (StrUtil.isBlank(requestBody)) {
throw new AuthenticationServiceException("无法获取输入信息");
}
// 小程序通过code登陆
if(SecurityConstants.SPRING_SECURITY_RESTFUL_TYPE_MINI_APP.equals(type)){
authRequest = Json.parseObject(requestBody, MiniAppAuthenticationToken.class);
}
// 账号密码登陆
else if (SecurityConstants.SPRING_SECURITY_RESTFUL_TYPE_ADMIN.equals(type)) {
authRequest = Json.parseObject(requestBody, AdminAuthenticationToken.class);
}
if (authRequest == null) {
throw new UnknownGrantTypeExceptionBase("未知的grant_type");
}
AbstractAuthenticationToken authRequest = authenticationTokenParser.parse(requestBody);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
@@ -81,12 +60,6 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
private String obtainParameter(HttpServletRequest request, String parameter) {
String result = request.getParameter(parameter);
return result == null ? "" : result;
}
private String getStringFromStream(HttpServletRequest req) {
ServletInputStream is;
try {
@@ -106,4 +79,8 @@ public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFil
return "";
}
}
public void setAuthenticationTokenParser(AuthenticationTokenParser authenticationTokenParser) {
this.authenticationTokenParser = authenticationTokenParser;
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
*
* https://www.gz-yami.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.security.provider;
import cn.hutool.core.util.StrUtil;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.security.constants.SecurityConstants;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.exception.BadCredentialsExceptionBase;
import com.yami.shop.security.exception.ImageCodeNotMatchExceptionBase;
import com.yami.shop.security.exception.UsernameNotFoundExceptionBase;
import com.yami.shop.security.exception.BaseYamiAuth2Exception;
import com.yami.shop.security.service.YamiUserDetailsService;
import com.yami.shop.security.token.AdminAuthenticationToken;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 后台管理员账号密码登陆
* @author LGH
*/
@Component
@AllArgsConstructor
public class AdminAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private final YamiUserDetailsService yamiUserDetailsService;
private final PasswordEncoder passwordEncoder;
@Override
protected UserDetails retrieveUser(String username, Authentication authentication) throws BaseYamiAuth2Exception {
UserDetails user;
try {
user = yamiUserDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundExceptionBase var6) {
throw new UsernameNotFoundExceptionBase("账号或密码不正确");
}
if (!user.isEnabled()) {
throw new UsernameNotFoundExceptionBase("账号已被锁定,请联系管理员");
}
return user;
}
@Override
protected void additionalAuthenticationChecks(UserDetails sysUser, Authentication authentication) throws BaseYamiAuth2Exception {
AdminAuthenticationToken adminAuthenticationToken = (AdminAuthenticationToken) authentication;
String kaptchaKey = SecurityConstants.SPRING_SECURITY_RESTFUL_IMAGE_CODE + adminAuthenticationToken.getSessionUUID();
String kaptcha = RedisUtil.get(kaptchaKey);
RedisUtil.del(kaptchaKey);
if(StrUtil.isBlank(adminAuthenticationToken.getImageCode()) || !adminAuthenticationToken.getImageCode().equalsIgnoreCase(kaptcha)){
throw new ImageCodeNotMatchExceptionBase("验证码有误");
}
String encodedPassword = sysUser.getPassword();
String rawPassword = authentication.getCredentials().toString();
// 密码不正确
if (!passwordEncoder.matches(rawPassword,encodedPassword)){
throw new BadCredentialsExceptionBase("账号或密码不正确");
}
}
@Override
protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
AdminAuthenticationToken result = new AdminAuthenticationToken(user, authentication.getCredentials());
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return AdminAuthenticationToken.class.isAssignableFrom(authentication);
}
@Override
protected App getAppInfo() {
return null;
}
}

View File

@@ -0,0 +1,13 @@
package com.yami.shop.security.provider;
import org.springframework.security.authentication.AbstractAuthenticationToken;
/**
* AuthenticationTokenParser
*
* @author hanfeng
* @date 2019-08-21
*/
public interface AuthenticationTokenParser {
AbstractAuthenticationToken parse(String authenticationTokenStr);
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
*
* https://www.gz-yami.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.security.provider;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.util.BooleanUtil;
import com.yami.shop.security.enums.App;
import com.yami.shop.security.exception.UsernameNotFoundExceptionBase;
import com.yami.shop.security.exception.WxErrorExceptionBase;
import com.yami.shop.security.model.AppConnect;
import com.yami.shop.security.service.YamiUser;
import com.yami.shop.security.service.YamiUserDetailsService;
import com.yami.shop.security.token.MiniAppAuthenticationToken;
import com.yami.shop.security.token.MyAuthenticationToken;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
/**
* 小程序登陆
* @author LGH
*/
@Component
@AllArgsConstructor
public class MiniAppAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private final YamiUserDetailsService yamiUserDetailsService;
private final WxMaService wxMaService;
@Override
protected void additionalAuthenticationChecks(UserDetails var1, Authentication authentication) throws AuthenticationException {
}
@Override
protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
MiniAppAuthenticationToken result = new MiniAppAuthenticationToken(user, authentication.getCredentials());
result.setDetails(authentication.getDetails());
return result;
}
@Override
protected UserDetails retrieveUser(String code, Authentication authentication) throws AuthenticationException {
YamiUser loadedUser = null;
// 如果使用debugger 模式则返回debugger的用户
if (BooleanUtil.isTrue(((MyAuthenticationToken)authentication).getDebugger())) {
loadedUser = new YamiUser("1" , "debuggerOpenId1" , this.getAppInfo().value(), true);
loadedUser.setDebugger(true);
return loadedUser;
}
WxMaJscode2SessionResult session = null;
AppConnect appConnect = new AppConnect();
appConnect.setAppId(this.getAppInfo().value());
try {
session = wxMaService.getUserService().getSessionInfo(code);
loadedUser = yamiUserDetailsService.loadUserByAppIdAndBizUserId(this.getAppInfo(),session.getOpenid());
} catch (WxErrorException e) {
throw new WxErrorExceptionBase(e.getMessage());
} catch (UsernameNotFoundExceptionBase var6) {
if (session == null) {
throw new WxErrorExceptionBase("无法获取用户登陆信息");
}
appConnect.setBizUserId(session.getOpenid());
appConnect.setBizUnionid(session.getUnionid());
yamiUserDetailsService.insertUserIfNecessary(appConnect);
}
if (loadedUser == null) {
loadedUser = yamiUserDetailsService.loadUserByAppIdAndBizUserId(this.getAppInfo(), appConnect.getBizUserId());
}
return loadedUser;
}
@Override
public boolean supports(Class<?> authentication) {
return MiniAppAuthenticationToken.class.isAssignableFrom(authentication);
}
@Override
protected App getAppInfo() {
return App.MINI;
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
*
* https://www.gz-yami.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.security.token;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* 系统用户账号密码登陆
*/
@Getter
@Setter
@NoArgsConstructor
public class AdminAuthenticationToken extends MyAuthenticationToken {
private String sessionUUID;
private String imageCode;
public AdminAuthenticationToken(UserDetails principal, Object credentials) {
super(principal, credentials, principal.getAuthorities());
}
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
*
* https://www.gz-yami.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package com.yami.shop.security.token;
import lombok.NoArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 二维码Token
*/
@NoArgsConstructor
public class MiniAppAuthenticationToken extends MyAuthenticationToken {
public MiniAppAuthenticationToken(UserDetails principal, Object credentials) {
super(principal, credentials, principal.getAuthorities());
}
}