1.授权码模式讲解
1.1:根据图片 我们看的出来 用户认证以后 我们拿到授权码 根据授权码 去获取token,那这个认证的过程 我们就不用去做了,我们只管用户登录成功以后 我们来拿到token
1.2:把password模式修改成 authorization_code 模式
password模式 如果你是看我这个文章来的 我们只需要 修改以下内容,如果你是新来了,可以去我的gitHub看这篇文章的源码 源码地址
1.3:修改 WebSecurityConfig 文件
package com.example.springcloudalibabaoauthauth.security;
import com.example.springcloudalibabaoauthauth.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 2 * @Author: ZhangShuai
* 3 * @Date: 2020/6/12 17:03
* 4
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//使用Security的认证管理器 认证操作security 给我们来做
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
//使用security的默认密码加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean //主要是配置这个Bean,用于授权服务器配置中注入
@Override
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
//配置security不拦截的请求 下面可以根据需求自定义 用,号隔开
@Override
public void configure(WebSecurity web) throws Exception {
// 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
//配置暴露出去的端点 不用登陆就可以访问
web.ignoring().antMatchers( "/GetToken", "/index.html", "/css/**", "/js/**",
"/images/**",
"/openid/login",
"/oauth/check_token", "/queryToken");
}
//增加了这个方法
@Override
public void configure(HttpSecurity http) throws Exception {
//禁止跨域请求
http.csrf().disable()
.formLogin()
.loginPage("/index.html") //不使用 security的登录页面 使用我们自己的登录页面
.loginProcessingUrl("/loginSubmit") //登录表单要提交的地址 这个地址不需要存在 保证表单提交的地址 和我们这里写的地址 是一样的
.defaultSuccessUrl("/loginSuccess") //登录 成功 要跳转的地址 这里 还有一个参数 是boolean类型的 默认为false
.and()
.authorizeRequests().anyRequest().authenticated();
}
}
1.4:修改 JwtUser 实体类的内容
package com.example.springcloudalibabaoauthauth.util;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* Created by zhangshuai on 2020/6/22.
*/
@Data
public class JwtUser extends User {
/**
* 用户id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
*账号
*/
private boolean accountNonExpired;
/**
*账号是否被锁定
*/
private boolean accountNonLocked;
/**
* 帐号是否过期
*/
private boolean credentialsNonExpired;
/**
* 帐号是否被禁用
*/
private boolean enabled;
/**
* 构造方法
* @param username
* @param password
* @param authorities
* @param accountNonExpired
* @param accountNonLocked
* @param credentialsNonExpired
* @param enabled
*/
public JwtUser(String username, String password, Collection<? extends GrantedAuthority> authorities, boolean accountNonExpired, boolean accountNonLocked, boolean credentialsNonExpired, boolean enabled) {
super(username, password, authorities);
this.accountNonExpired = accountNonExpired;
this.accountNonLocked = accountNonLocked;
this.credentialsNonExpired = credentialsNonExpired;
this.enabled = enabled;
}
/**
* 让boolean的都变为true
* @param username 用户名
* @param password 密码
* @param authorities 权限
*/
public JwtUser(String username, String password,
Collection<? extends GrantedAuthority> authorities) {
this(username, password, authorities, true, true, true,true );
}
}
1.5:修改 application.yml
server:
port: 9090
spring:
application:
name: springcloud-oauth-auth
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.3.37/test?useUnicode=true&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull
password: zhangxiaoyun123
username: root
resources:
static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/
thymeleaf:
encoding: UTF-8
suffix: .html
cache: false
# 配置前缀时必须加templates,浏览器访问页面时不加templates路径
prefix: classpath:/templates/
#cloud:
# nacos:
# discovery:
# #指定nacos server的地址
# server-addr: 127.0.0.1:8848
logging:
level:
cloud.zxy.system: debug
2.增加 controller层
2.1 :AuthToken 定义返回令牌信息
package com.example.springcloudalibabaoauthauth.util;
import lombok.Data;
/**
* Created by zhangshuai on 2020/6/22.
*/
@Data
public class AuthToken {
/**
* access_token
*/
private String access_token;
/**
* 刷新令牌token
*/
private String refresh_token;
/**
* jwt令牌
*/
private String jwt_token;//jwt令牌
/**
* 令牌到期时间
*/
private Integer expires_in;
/**
* 错误
*/
private String error;
/**
* 申请令牌的错误信息
*/
private String error_description;
}
2.2 LoginController:使用code获取令牌的一些逻辑
package com.example.springcloudalibabaoauthauth.controller;
import com.example.springcloudalibabaoauthauth.util.AuthToken;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
/**
* 2 * @Author: ZhangShuai
* 3 * @Date: 2020/6/20 11:22
* 4
*/
@RestController
@Slf4j
public class LoginController {
@Autowired
private RestTemplate restTemplate;
/*登录的方法*/
@GetMapping("loginSuccess")
public String login(){
return "ok";
}
/**
* 前端 可以根据这个地址 拿到token
* 获取code 的回调地址
* @param code
* @return
*/
@GetMapping("GetToken")
public AuthToken GetToken(@RequestParam String code){
//这里可以让前端 给你传一个 值 拿到这个值 可以进行回调到 这个url
log.info("code is " + code);
String authUrl = "http://localhost:9090/oauth/token";
//定义header
LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
String httpBasic = getHttpBasic();
header.add("Authorization", httpBasic);
//定义body
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("code", code);
body.add("redirect_uri", "http://localhost:9090/GetToken");
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
//调用登录认证服务 生成jwt令牌
ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);
//申请令牌信息
AuthToken authToken = makeAuthToken(exchange);
return authToken;
}
/**
* 设置token值
*
* @param exchange 远程调用结果
* @return
*/
private AuthToken makeAuthToken(ResponseEntity<Map> exchange) {
Map bodyMap = exchange.getBody();
AuthToken authToken = new AuthToken();
if (bodyMap == null ||
bodyMap.get("access_token") == null ||
bodyMap.get("refresh_token") == null ||
bodyMap.get("jti") == null ||
bodyMap.get("expires_in") == null
) {
authToken.setError((String) bodyMap.get("error"));
authToken.setError_description((String) bodyMap.get("error_description"));
return authToken;
}
authToken.setAccess_token((String) bodyMap.get("access_token"));//用户身份令牌
authToken.setRefresh_token((String) bodyMap.get("refresh_token"));//刷新令牌
authToken.setJwt_token((String) bodyMap.get("jti"));//jwt令牌
return authToken;
}
/*获取httpBasic的串*/
private String getHttpBasic() {
String string = "client" + ":" + "admin";
//将串进行base64编码
byte[] encode = Base64Utils.encode(string.getBytes());
return "Basic " + new String(encode);
}
}
2.3 resources:下面增加 static 和templates
下载链接:https://pan.baidu.com/s/1aG4APvMFluyDXu-YxD23zg
提取码:vdxj
3.测试授权码获取token
3.1:首先通过浏览器访问下面的地址获取授权码(code)
http://localhost:9090/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://localhost:9090/GetToken
注(这里的cilent 还有response_type 和 redirect_uri)必须在数据库配置 oauth_client_details
当我们访问上面地址的时候 会跳转到 首页让我们登录
3.2 登录成功 返回token
注意事项:
redirect_uri :必须保证和数据库的一致 不然会报这个错误
cilent :必须和数据库的一致
response_type=code:必须保证你配置了 授权码登录
源码地址