赞
踩
主要说明:
基于若依springcloud微服务框架的2.1版本
嫌弃缩进不舒服的,直接访问我的博客站点:
http://binarydance.top//aticle_view.html?aticle_id=603216759344082944&t=1623984554005
一开始网上教程一堆,都是各抄各的,有的直接代码缺少,有的直接不可以用(MLGB的),于是乎去spring官网找找看,还真找到,最终自己配置搭建成功跑了一遍demo,美滋滋。一些HTML文件还是网上的,见谅~
由于是springcloud项目(注册和配置中心是nacos),认证中心在auth模块,自己demo搭建测试直接在auth模块,没有走网关gateway
目前测试的scope只有一个:server,见表:sys_oauth_client_details
sys_oauth_client_details表:
CREATE TABLE `sys_oauth_client_details` (
`client_id` varchar(255) NOT NULL COMMENT '终端编号',
`resource_ids` varchar(255) DEFAULT NULL COMMENT '资源ID标识',
`client_secret` varchar(255) NOT NULL COMMENT '终端安全码',
`scope` varchar(255) NOT NULL COMMENT '终端授权范围',
`authorized_grant_types` varchar(255) NOT NULL COMMENT '终端授权类型',
`web_server_redirect_uri` varchar(255) DEFAULT NULL COMMENT '服务器回调地址',
`authorities` varchar(255) DEFAULT NULL COMMENT '访问资源所需权限',
`access_token_validity` int(11) DEFAULT NULL COMMENT '设定终端的access_token的有效时间值(秒)',
`refresh_token_validity` int(11) DEFAULT NULL COMMENT '设定终端的refresh_token的有效时间值(秒)',
`additional_information` varchar(4096) DEFAULT NULL COMMENT '附加信息',
`autoapprove` tinyint(4) DEFAULT NULL COMMENT '是否登录时跳过授权',
`origin_secret` varchar(255) NOT NULL COMMENT '终端明文安全码',
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='终端配置表';
INSERT INTO `sys_oauth_client_details` VALUES ('web', '', '$2a$10$t.Hw9Nu.lWp5iK9I7MZRCuX9EeV2DNu6xj9wunfD5ZclvQSdoBo5O', 'server', 'password,refresh_token,authorization_code,implicit,client_credentials', 'https://www.baidu.com', NULL, 604800, 1209600, NULL, NULL, '123456');
Spring Security Oauth2 的maven坐标:
<!-- Spring Security Oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
项目配置:
# Tomcat server: port: 9200 # Spring spring: application: # 应用名称 name: my-auth profiles: # 环境配置 active: dev cloud: nacos: discovery: # 服务注册地址 server-addr: 192.168.1.39:8848 config: # 配置中心地址 server-addr: 192.168.1.39:8848 # 配置文件格式 file-extension: yml # 共享配置 shared-configs: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} main: allow-bean-definition-overriding: true
引入模板解析引擎:
<!-- thymeleaf 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
AuthorizationServerConfigurerAdapter无需改动,你的是什么就是什么:
@Configuration
@EnableAuthorizationServer
@Slf4j
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
// 无需改动,你的是什么就是什么
......
}
上面是基础,关键来了:
resources下面建立static目录,放置登录页面(注意:form表单action="/login"):base-login.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <style> .login-container { margin: 50px; width: 100%; } .form-container { margin: 0px auto; width: 50%; text-align: center; box-shadow: 1px 1px 10px #888888; height: 300px; padding: 5px; } input { margin-top: 10px; width: 350px; height: 30px; border-radius: 3px; border: 1px #E9686B solid; padding-left: 2px; } .btn { width: 350px; height: 35px; line-height: 35px; cursor: pointer; margin-top: 20px; border-radius: 3px; background-color: #E9686B; color: white; border: none; font-size: 15px; } .title{ margin-top: 5px; font-size: 18px; color: #E9686B; } </style> <body> <div class="login-container"> <div class="form-container"> <form class="form-signin" method="post" action="/login"> <h2 class="form-signin-heading">用户登录</h2> <p> <label for="username" class="sr-only">用户名</label> <input type="text" id="username" name="username" class="form-control" placeholder="用户名" required autofocus> </p> <p> <label for="password" class="sr-only">密码</label> <input type="password" id="password" name="password" class="form-control" placeholder="密码" required> </p> <button class="btn btn-lg btn-primary btn-block" type="submit">登 录</button> </form> </div> </div> </body> </html>
配置WebSecurityConfigurerAdapter:
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.authentication.dao.DaoAuthenticationProvider; 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.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; /** * Security 安全认证相关配置 */ @Order(99) @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 配置修改hideUserNotFoundExceptions = false,不隐藏usernameNotFundExceptions * @return */ @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setHideUserNotFoundExceptions(false); provider.setUserDetailsService(userDetailsService); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin() .loginPage("/base-login.html") //自定义的登录页面 **重要** .loginProcessingUrl("/login") //原始的处理登录的URL,保持和base-login.html的form表单的action一致 **重要** .permitAll() //放开 **重要** .and() .requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")// **重要** .and() .authorizeRequests() .antMatchers( "/xxx/**",).permitAll()//一些需要放开的URL .anyRequest().authenticated() .and().headers().frameOptions().disable() .and().csrf().disable(); } }
到此登录页面自定义完成。别急,还有自定义授权页面呢。
在resources目录下面建立templates目录,放置授权页面(注意:form表单action="/oauth/authorize"):base-grant.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>授权</title> </head> <style> html{ padding: 0px; margin: 0px; } .title { background-color: #E9686B; height: 50px; padding-left: 20%; padding-right: 20%; color: white; line-height: 50px; font-size: 18px; } .title-left{ float: right; } .title-right{ float: left; } .title-left a{ color: white; } .container{ clear: both; text-align: center; } .btn { width: 350px; height: 35px; line-height: 35px; cursor: pointer; margin-top: 20px; border-radius: 3px; background-color: #E9686B; color: white; border: none; font-size: 15px; } </style> <body style="margin: 0px"> <div class="title"> <div class="title-right">oauth2授权</div> <div class="title-left"> <a href="#help">帮助</a> </div> </div> <div class="container"> <h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'"></h3> <p>昵称,头像和性别</p> 授权后表明你已同意 <a href="#boot" style="color: #E9686B">服务协议</a> <form method="post" action="/oauth/authorize"> <input type="hidden" name="user_oauth_approval" value="true"> <div th:each="item:${scopes}"> <input type="radio" th:name="'scope.'+${item}" value="true" checked>同意 <input type="radio" th:name="'scope.'+${item}" value="false" >拒绝 </div> <input name="authorize" value="同意/授权" type="submit"> </form> </div> </body> </html>
同时,controller覆盖oauth2的方法:
import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.security.oauth2.provider.AuthorizationRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.servlet.ModelAndView; @Controller //必须配置 @SessionAttributes("authorizationRequest") public class BootGrantController { @RequestMapping("/oauth/confirm_access") public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception { AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest"); ModelAndView view = new ModelAndView(); view.setViewName("base-grant"); //自定义页面名字,resources\templates\base-grant.html view.addObject("clientId", authorizationRequest.getClientId()); view.addObject("scopes",authorizationRequest.getScope()); return view; } }
至此,自定义登录页面、授权页面完成。
①获取授权码code(注意:client_id=web):
http://192.168.1.39:9200/oauth/authorize?response_type=code&client_id=web&redirect_uri=https://www.baidu.com&scope=server
第一次访问上面地址获取授权码code,先会跳转至自定义登录页面:
输入账号密码,跳转至授权页面:
同意之后,跳转到我们设定的redirect_uri=https://www.baidu.com,同时获取授权码code(牛逼,这样图片也违规,去我博客站点看吧~):
②根据上面获取的授权码code=rLePQ8获取访问的token:
至此流程完成,等到我们的token
③根据token去访问你需要的资源吧。
特别说明的是:第一次授权以后获取授权码code,之后获取授权码code直接收入账号、密码后立即返回授权码code,并不会再一次要求用户进入授权页面(base-grant.html)允许、拒绝选择操作。
如果希望默认就是授权,不需要跳出授权页面(base-grant.html),那么sys_oauth_client_details表中,autoapprove(是否登录时跳过授权)设置为true(值:1)即可。
Spring Security Oauth2 授权码模式下 自定义登录、授权页面(演示)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。