Spring Security Oauth2 授权码模式下 自定义登录、授权页面

8 篇文章 1 订阅
3 篇文章 0 订阅

主要说明:基于若依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">&nbsp;&nbsp;</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 授权码模式下 自定义登录、授权页面(演示)

  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
要关闭Spring Security OAuth2的默认登录表单并自定义登录页面,您需要执行以下操作: 1. 创建一个自定义登录页面,例如/login。 2. 在Spring Security配置中禁用默认登录表单,如下所示: ``` @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } ``` 在上面的代中,`.antMatchers("/login").permitAll()`表示允许所有用户访问/login页面,并且`.formLogin().loginPage("/login").permitAll()`指定了自定义登录页面的URL。 3. 创建一个自定义登录表单,该表单将POST请求发送到Spring Security的/oauth/token端点,以便验证用户凭据。以下是示例HTML代: ``` <form method="post" action="/oauth/token"> <div> <label for="username">Username:</label> <input type="text" id="username" name="username"/> </div> <div> <label for="password">Password:</label> <input type="password" id="password" name="password"/> </div> <div> <input type="submit" value="Login"/> </div> <input type="hidden" name="grant_type" value="password"/> <input type="hidden" name="scope" value="read write"/> <input type="hidden" name="client_id" value="my-client"/> <input type="hidden" name="client_secret" value="my-secret"/> </form> ``` 在上面的代中,`<input type="hidden" name="grant_type" value="password"/>`表示使用密授权类型进行身份验证,`<input type="hidden" name="scope" value="read write"/>`指定访问令牌的作用域,`<input type="hidden" name="client_id" value="my-client"/>`和`<input type="hidden" name="client_secret" value="my-secret"/>`指定客户端ID和密钥。 4. 使用您选择的Web框架创建/login端点,并在该端点上呈现自定义登录表单。当用户提交表单时,将使用Spring Security OAuth2的/oauth/token端点验证用户凭据并返回访问令牌。 请注意,此方案仅适用于密授权类型。如果您需要其他授权类型(例如授权或隐式授权),则需要实现相应的授权流程。
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值