瀏覽代碼

更新文档

chendt 1 年之前
父節點
當前提交
39505e5c99

+ 1 - 0
doc/README.md

@@ -42,6 +42,7 @@
 ### 1.2 安装jdk + mysql + redis + maven
 
 如果不了解怎么安装jdk的,可以参考 [菜鸟教程的java相关](https://www.runoob.com/java/java-environment-setup.html)
+- 教程展示的是oracle,需要自行搜索openjdk的下载链接,下载jdk17版本
 
 如果不了解怎么安装mysql的,可以参考  [菜鸟教程的mysql相关](https://www.runoob.com/mysql/mysql-install.html) 
 

+ 0 - 251
doc/认证与授权/从授权开始看源码.md

@@ -1,251 +0,0 @@
->  如果不理解oauth协议的推荐阅读 阮一峰的[理解OAuth 2.0](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)
-
-当然,我们也要简单介绍下oauth的运行流程:
-
-```
-     +--------+                               +---------------+
-     |        |--(A)- Authorization Request ->|   Resource    |
-     |        |                               |     Owner     |
-     |        |<-(B)-- Authorization Grant ---|               |
-     |        |                               +---------------+
-     |        |
-     |        |                               +---------------+
-     |        |--(C)-- Authorization Grant -->| Authorization |
-     | Client |                               |     Server    |
-     |        |<-(D)----- Access Token -------|               |
-     |        |                               +---------------+
-     |        |
-     |        |                               +---------------+
-     |        |--(E)----- Access Token ------>|    Resource   |
-     |        |                               |     Server    |
-     |        |<-(F)--- Protected Resource ---|               |
-     +--------+                               +---------------+
-```
-
-运行流程如下图,摘自RFC 6749。
-
-
-
-- (A)用户打开客户端以后,客户端要求用户给予授权。
-- (B)用户同意给予客户端授权。
-- (C)客户端使用上一步获得的授权,向认证服务器申请令牌。
-- (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
-- (E)客户端使用令牌,向资源服务器申请获取资源。
-- (F)资源服务器确认令牌无误,同意向客户端开放资源。
-
-
-
-我们是对内的系统,并不需要那么复杂的流程,所以我们看下oauth的授权模式当中的密码模式:
-
-```
-     +----------+
-     | Resource |
-     |  Owner   |
-     |          |
-     +----------+
-          v
-          |    Resource Owner
-         (A) Password Credentials
-          |
-          v
-     +---------+                                  +---------------+
-     |         |>--(B)---- Resource Owner ------->|               |
-     |         |         Password Credentials     | Authorization |
-     | Client  |                                  |     Server    |
-     |         |<--(C)---- Access Token ---------<|               |
-     |         |    (w/ Optional Refresh Token)   |               |
-     +---------+                                  +---------------+
-```
-
-这里的流程相对就比较简单了:
-
-(A)用户向客户端提供用户名和密码。
-
-(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
-
-(C)认证服务器确认无误后,向客户端提供访问令牌。
-
-
-
-现在将简单的转换下思路:
-
-- `Resource Owner`:资源拥有者,拥有订单,购物车等数据的人,既用户
-
-- `Client`:客户端,浏览器
-
-- `Authorization Server`:认证服务器,也就是服务器咯。
-
-
-
-在此A、B、C三个流程就变成了:
-
-(A)用户在浏览器输入用户名和密码。
-
-(B)浏览器将用户名和密码发给服务器,向后者请求令牌(token)。
-
-(C)服务器确认无误后,返回token给用户。
-
-
-
-但是根据标准的流程,并没有验证码之类的容身之地。而`spring security oauth2` 给我们提供的只能是标准的流程,所以我们对代码进行一些适配,能够适应我们自己的需求。
-
-
-
-## spring的部分源码
-
-我们先来看下`spring security oauth2`的部分源码
-
-首先我们直接进行授权的时候,调用的url大概为:`http://localhost:8080/oauth/token?username=user_1&password=123456&grant_type=password&scope=select&client_id=client_2&client_secret=123456`,那么授权肯定是与该链接相关联的。基于这个猜测,我们去寻找源码吧。
-
-
-
-在`idea`中使用全局搜索,搜索 字符串`"/oauth/token"`(带着引号),发现了一个类,似乎与这个请求有关 `ClientCredentialsTokenEndpointFilter`
-
-```java
-public class ClientCredentialsTokenEndpointFilter extends AbstractAuthenticationProcessingFilter {
-
-	public ClientCredentialsTokenEndpointFilter() {
-		this("/oauth/token");
-	}
-}
-```
-
-```
-ClientCredentialsTokenEndpointFilter
-	---> AbstractAuthenticationProcessingFilter
-		---> GenericFilterBean
-			---> Filter
-```
-
-发现,这个类是一个 `Filter` 也就是过滤器,通过这个过滤器,过滤请求,那么,我们去看看`doFilter`方法咯,`doFilter` 在 `ClientCredentialsTokenEndpointFilter` 的父类 `AbstractAuthenticationProcessingFilter` 上。
-
-我们看看`AbstractAuthenticationProcessingFilter`:
-
-```java
-	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
-			throws IOException, ServletException {
-
-		HttpServletRequest request = (HttpServletRequest) req;
-		HttpServletResponse response = (HttpServletResponse) res;
-        // 如果不是认证的请求,直接下一个filter
-        // 这里是怎么判断是否是下一个请求呢?
-        // 答:看看url是不是上面ClientCredentialsTokenEndpointFilter 创建时传过来的url,也就是 /oauth/token
-		if (!requiresAuthentication(request, response)) {
-			chain.doFilter(request, response);
-			return;
-		}
-
-		Authentication authResult;
-		try {
-            // 调用attemptAuthentication 方法,返回一个 Authentication 的实现类,也就是认证信息,这个实现类非常重要!!! 
-			authResult = attemptAuthentication(request, response);
-            // 如果找不到,那就没了
-			if (authResult == null) {
-				return;
-			}
-		}
-		// 调用成功的方法
-		successfulAuthentication(request, response, chain, authResult);
-	}
-```
-
-这里最重要的方法`attemptAuthentication` 生成一个授权信息,能够返回,则证明登录已经成功了,所以真正的登录与这里有关。
-
-我们回到`ClientCredentialsTokenEndpointFilter` 这个实现类里面看看`attemptAuthentication`方法吧
-
-```java
-	@Override
-	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
-			throws AuthenticationException, IOException, ServletException {
-		
-		// ======精简没啥用的方法========
-		
-		// 构造一个UsernamePasswordAuthenticationToken 
-		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId,
-				clientSecret);
-		// 调用认证方法进行认证
-		return this.getAuthenticationManager().authenticate(authRequest);
-
-	}
-```
-
-我们通过添加断点可以发现 `this.getAuthenticationManager()` 是一个`ProviderManager` 对象,我们看下
-
-`this.getAuthenticationManager().authenticate()` 里面的 `authenticate`
-
-```java
-public class ProviderManager{
-    public Authentication authenticate(Authentication authentication)
-        throws AuthenticationException {
-        
-        Authentication result = null;
-        
-        for (AuthenticationProvider provider : getProviders()) {
-            // 在一堆的provider中寻找到一个合适的授权提供者
-			if (!provider.supports(toTest)) {
-				continue;
-			}
-			// 由授权提供者进行授权
-			result = provider.authenticate(authentication);
-        }
-        if (result != null) {
-			return result;
-		}
-    }
-}
-```
-
-一路追踪到这里,我们发现,实际上,是通过`provider.supports(toTest)` 寻找一个合适的授权提供者,使用`provider.authenticate(authentication)`就行授权,而`supports` 的依据是通过之前生成的token来判断是否支持:
-
-```java
-	public boolean supports(Class<?> authentication) {
-		return (UsernamePasswordAuthenticationToken.class
-				.isAssignableFrom(authentication));
-	}
-```
-
-
-
-我们整理下这几个流程
-
-```
-ClientCredentialsTokenEndpointFilter.doFilter() 
-	--> AbstractAuthenticationProcessingFilter.attemptAuthentication()
-		--> ProviderManager.authenticate()
-			--> AuthenticationProvider.supports()
-			--> AuthenticationProvider.authenticate()
-```
-
-
-
-我们可以看到这里主要就是干了几件事情
-
-- 通过filter 确定登录要过滤的url
-- 通过filter 确定生成的`AbstractAuthenticationToken` 比如 `UsernamePasswordAuthenticationToken`
-- 通过生成的`AbstractAuthenticationToken` 确定`AuthenticationProvider`
-- 通过`AuthenticationProvider` 最后调用 `authenticate()`方法最后进行授权
-
-
-
-最后通过`RequestMapping` 返回
-
-```java
-@FrameworkEndpoint
-public class TokenEndpoint extends AbstractEndpoint{
-    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
-	public ServerResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
-	Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
-
-
-		String clientId = getClientId(principal);
-		ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
-
-		TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
-
-		OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
-
-		return getResponse(token);
-	}
-}
-```
-

+ 0 - 125
doc/认证与授权/自己写个授权的方法-开源版.md

@@ -1,125 +0,0 @@
-通过【从授权开始看源码】我们可以看到这里主要就是干了几件事情
-
-- 通过filter 确定登录要过滤的url
-- 通过filter 确定生成的`AbstractAuthenticationToken` 比如 `UsernamePasswordAuthenticationToken`
-- 通过生成的`AbstractAuthenticationToken` 确定`AuthenticationProvider`
-- 通过`AuthenticationProvider` 最后调用 `authenticate()`方法最后进行授权
-
-
-
-根据上面我们对自己对代码进行了一些封装
-
-我们先来看`LoginAuthenticationFilter`
-
-```java
-public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
-
-    private AuthenticationTokenParser authenticationTokenParser;
-
-    @Override
-    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
-
-        AbstractAuthenticationToken authRequest  =  authenticationTokenParser.parse(requestBody);
-
-        return this.getAuthenticationManager().authenticate(authRequest);
-    }
-
-
-    public void setAuthenticationTokenParser(AuthenticationTokenParser authenticationTokenParser) {
-        this.authenticationTokenParser = authenticationTokenParser;
-    }
-}
-```
-
-这里的登录继承了`UsernamePasswordAuthenticationFilter`  里面写了
-
-```java
-public UsernamePasswordAuthenticationFilter() {
-   super(new AntPathRequestMatcher("/login", "POST"));
-}
-```
-
-这就是为什么登录的接口是`/login`的原因
-
-
-
-我们再来看看生成`AbstractAuthenticationToken `的方法
-
-`AbstractAuthenticationToken authRequest = authenticationTokenParser.parse(requestBody);`
-
-这里决定了生成什么token,将会决定后面的`AuthenticationProvider`
-
-
-
-我们先来看`AdminAuthenticationProvider`
-
-```
-    @Override
-    public boolean supports(Class<?> authentication) {
-        return AdminAuthenticationToken.class.isAssignableFrom(authentication);
-    }
-```
-
-这里决定`AdminAuthenticationToken` 是通过`AdminAuthenticationProvider` 进行校验
-
-
-
-再来看下完整的`AdminAuthenticationProvider` 你就知道验证码在哪里校验的了,是不是很简单
-
-```java
-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;
-    }
-
-}
-
-```
-