1.認証サーバーを構築します
新しいspringboot2プロジェクトを作成し、次の2つの構成クラスを追加してサーバーを起動します
1. AuthorizationServerConfiguration
主に3つの構成方法があります
(1)ここでは、2つのAPIのアクセス制限を解除し、クライアントのIDとパスワードをフォームフォームに直接書き込むことができるようにします。
-クライアントはユーザーではなく、サードパーティのWebサイトを参照しています。クライアントを使用してコードでクライアントを表す
(2)2番目の方法は、クライアントの情報を指定するために使用されます
(3)3番目の方法は、トークンの保存場所を構成するために使用されます
2. WebSecurityConfiguration
主に3つの豆を提供するために使用されます
これで、次の2つのAPIを使用してテストします
(1)http:// localhost:37001 / oauth / token
(2)http:// localhost:37001 / oauth / check_token
(コード)Lanzous Cloud:https://wws.lanzous.com/iG4xHlo426h
変更できる場所がいくつかあります
(データベース)Lanzous Cloud:https://wws.lanzous.com/ibQBPlo420b
(1)クライアント情報はデータベースに保存されます
(2)トークンはredisに保存されます
(3)ユーザー情報はデータベースに保存されます
ユーザー名を入力して、UserDetailsインターフェースの実装クラスに戻ります
2つ目は、ユーザーパスワードの暗号化を変更する
(1)1つの方法は、カスタムPasswordEncoderを使用することです
(2)別の方法は、ユーザーログイン認証のロジックをオーバーライドすることです
ここにソルト暗号化を追加するには、md5を使用します
package hlmio.oauth2.conf.security.login;
import hlmio.oauth2.model.User;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* 登录认证的Provider,自定义实现{@link AuthenticationProvider} <br>
*/
@Log
@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsServiceImpl;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// http请求的账户密码
String username = authentication.getName();
String password = (String) authentication.getCredentials();
// 根据用户名查询数据库
UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
log.info(String.format("[http请求的账户密码]: %s/%s", username, password));
if (userDetails == null) {
throw new BadCredentialsException("用户名未找到");
}
String rawPassword = password + ((User)userDetails).getSalt();
String encodedPassword = "{MD5}"+userDetails.getPassword();
if(!passwordEncoder.matches(rawPassword,encodedPassword)){
throw new BadCredentialsException("密码不正确");
}
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
/**
* 是否支持处理当前Authentication对象类型
*/
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
3、ユーザー権限認証を変更します
主にaccessDecisionManagerの権限判断ロジックを書き直すため
(1)MethodSecurityMetadataSourceは、メソッドアノテーションに書き込まれたパーミッション名を読み取るために使用されます
package hlmio.oauth2.conf.security.access;
import hlmio.oauth2.conf.security.other.NoAccess;
import hlmio.oauth2.conf.security.other.NoAuth;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@Component
public class MyMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return Collections.emptyList();
}
if(targetClass.getName().indexOf("hlmio.oauth2.api") == -1){
return Collections.emptyList();
}
System.out.println("method: '" + method.getName() + "' class: '" + targetClass + "'");
ArrayList<ConfigAttribute> attrs = new ArrayList(5);
// region 权限(1):控制器名::方法名
// 获取控制器的名称
String controllerName_all = targetClass.getName();
String[] controllerName_arr = controllerName_all.split("\\.");
String controllerName = controllerName_arr[controllerName_arr.length-1];
// 获取方法名
String funcName = method.getName();
// 权限字段的名称
String permName = controllerName + "::" + funcName;
System.out.println(permName);
attrs.add(new MyConfigAttribute(permName));
// endregion
// region 权限(2):注解NoAuth
NoAuth noAuth = (NoAuth)this.findAnnotation(method, targetClass, NoAuth.class);
NoAccess noAccess = (NoAccess)this.findAnnotation(method, targetClass, NoAccess.class);
if(noAuth!=null){
attrs.add(new MyConfigAttribute("NoAuth"));
}
if(noAccess!=null){
attrs.add(new MyConfigAttribute("NoAccess"));
}
// endregion
attrs.trimToSize();
return attrs;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private <A extends Annotation> A findAnnotation(Method method, Class<?> targetClass, Class<A> annotationClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on specific method: " + specificMethod);
return annotation;
} else {
if (specificMethod != method) {
annotation = AnnotationUtils.findAnnotation(method, annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + method);
return annotation;
}
}
annotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);
if (annotation != null) {
this.logger.debug(annotation + " found on: " + specificMethod.getDeclaringClass().getName());
return annotation;
} else {
return null;
}
}
}
}
フレームワークに必要なパーミッションアイテムクラス
package hlmio.oauth2.conf.security.access;
import lombok.Setter;
import org.springframework.security.access.ConfigAttribute;
public class MyConfigAttribute implements ConfigAttribute {
@Setter
String attribute;
MyConfigAttribute(){
}
MyConfigAttribute(String attribute){
this.attribute = attribute;
}
@Override
public String getAttribute(){
return attribute;
}
}
(2)AccessDecisionManagerで許可判断を書き込むロジック
package hlmio.oauth2.conf.security.access;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
List<String> targetAccessList = configAttributes.stream()
.map( i -> i.getAttribute())
.collect(Collectors.toList());
// 放行无需登录和权限的注解
if(targetAccessList.contains("NoAuth")){
return;
}
// 拒绝未登录用户
if (authentication == null || "anonymousUser".equals(authentication.getName())) {
throw new AccessDeniedException("当前访问需要登录");
}
// 放行无需权限的注解
if(targetAccessList.contains("NoAccess")){
return;
}
List<String> userAccessList = authentication.getAuthorities().stream()
.map( i -> i.getAuthority())
.collect(Collectors.toList());
for (String s : userAccessList) {
for (String s1 : targetAccessList) {
if(s != null){
if(s.equals(s1)){
return;
}
}
}
}
throw new AccessDeniedException("当前访问没有权限");
}
public boolean supports(ConfigAttribute attribute) {
return true;
}
public boolean supports(Class<?> clazz) {
return MethodInvocation.class.isAssignableFrom(clazz);
}
}
github:https://github.com/hl-mio/a1