文章目录

  • 介绍
  • 实现功能
  • 系统环境配置
  • 开发步骤
  • 引入jar包
  • 定义登录和授权页面
  • 定义一个关于登录和授权的控制器
  • 配置页面授权
  • 测试
  • 项目源码


介绍

在上一章节中,。我们是实现了简单的oauth2服务提供商。但还不能满足现实需要,在现实中,我们需要定制特质的登录和授权页面。下面将讲解如何自定义。

实现功能

能自定义登录和授权页面。

系统环境配置
  1. JDK1.8+
  2. mysql5.6+
  3. Redis
开发步骤
引入jar包

首先引入的是thymeleaf。用于html页面开发

<!--页面要用到的框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

其他包读者可查阅源码了解。

定义登录和授权页面

1.登录页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>登录</title>
</head>
<body>
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<!--spring security 默认处理用户名密码就是/login,可以自定义,需要loginProcessingUrl()-->
<p style="color: red" th:if="${param.error}">用户名或密码错误</p>
<form action="/authentication/form" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td>
                <label><input type="text" name="username"/></label>
            </td>
        </tr>
        <tr>
            <td>密码:</td>
            <td>
                <label><input type="password" name="password"/></label>
            </td>
        </tr>

        <tr>
            <td colspan="2">
                <button type="submit">登录</button>
            </td>
        </tr>
    </table>
</form>


</body>
</html>
  1. 授权页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>确认授权页面</title>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"/>
    <link rel="stylesheet" href="//i.gtimg.cn/vipstyle/frozenui/2.0.0/css/frozen.css"/>

    <style>
        .block{
            position: relative;
        }
        .ui-notice{
            position: relative;
            padding:20px 15px;
            box-sizing: border-box;
        }
        .ui-notice p{
            color:#333;
            font-weight: 600;
        }
        .ui-btn-primary{
            background-color: #02cd93;
            border-color:#02cd93;;
        }
        .ui-notice-btn{
            padding:50px 0px 15px;
        }
    </style>
</head>
<body>
<div class="block">
    <section class="ui-notice">
        <i class="icon icon-notice"></i>
        <p>是否授权:<span th:text="${session.authorizationRequest.clientId}">clientId</span></p>
        <div class="ui-notice-btn">
            <form id='confirmationForm' name='confirmationForm' action="/oauth/authorize" method='post'>
                <input name='user_oauth_approval' value='true' type='hidden'/>
                <!--写好授权访问领域-->
                <input th:name="${s}" value="true" type="hidden" th:each="s : ${scopeList}"/>
                <input class="ui-btn-primary ui-btn-lg ui-btn-primary" name='authorize' value='授权' type='submit'/>
            </form>
        </div>
    </section>
</div>
</body>
</html>

作者只是简单定义页面而已。读者可以根据需求自己定义。

定义一个关于登录和授权的控制器

在这个类中,需要处理登录请求,页面的跳转,以及授权页面。

/**
 * @author lvhaibao
 * @description 处理登录和授权的控制器
 * @date 2018/12/26 0026 17:31
 */
@Slf4j
@Controller
@SessionAttributes({"authorizationRequest"})
public class AuthPageController {

    private RequestCache requestCache = new HttpSessionRequestCache();
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Autowired
    private SecurityProperties securityProperties;


    /**
     * 当用户没登录的时候,会经过这个请求,在这个请求中可以处理一些逻辑
     *
     * @param request  request
     * @param response response
     * @return ResultModel
     * @throws IOException IOException
     */
    @RequestMapping(FromLoginConstant.LOGIN_PAGE)
    @ResponseBody
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public ResultModel requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {

        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (null != savedRequest) {
            String targetUrl = savedRequest.getRedirectUrl();
            log.info("引发跳转的请求是:" + targetUrl);
            redirectStrategy.sendRedirect(request, response, FromLoginConstant.AFTER_LOGING_PAGE);
        }
        //如果访问的是接口资源
        return ResultModel.fail(401, "访问的服务需要身份认证,请引导用户到登录页");
    }


    @RequestMapping(FromLoginConstant.AFTER_LOGING_PAGE)
    public String login() {
        return securityProperties.getOauthLogin().getOauthLogin();
    }


    /**
     * 自定义授权页面,注意:一定要在类上加@SessionAttributes({"authorizationRequest"})
     *
     * @param model   model
     * @param request request
     * @return String
     * @throws Exception Exception
     */
    @RequestMapping("/oauth/confirm_access")
    public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
        @SuppressWarnings("unchecked")
        Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
        List<String> scopeList = new ArrayList<>();
        if (scopes != null) {
            scopeList.addAll(scopes.keySet());
        }
        model.put("scopeList", scopeList);
        return securityProperties.getOauthLogin().getOauthGrant();
    }

在这个类中,定义了一些常量或者配置。读者可以很根据源码找到相关的配置信息的类。

配置页面授权

在上面定义的请求中,需要对这类请求进行处理。

/**
 * @author lvhaibao
 * @description 浏览器配置
 * @date 2018/12/25 0025 10:53
 */
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityProperties securityProperties;


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
                .formLogin().loginPage(FromLoginConstant.LOGIN_PAGE).loginProcessingUrl(FromLoginConstant.LOGIN_PROCESSING_URL)
                //允许访问
                .and().authorizeRequests().antMatchers(
                "/user/hello",
                FromLoginConstant.LOGIN_PROCESSING_URL,
                FromLoginConstant.LOGIN_PAGE,
                securityProperties.getOauthLogin().getOauthLogin(),
                securityProperties.getOauthLogin().getOauthGrant()).permitAll().anyRequest().authenticated()
                //禁用跨站伪造
                .and().csrf().disable();

    }


}
测试
  1. 测试过程和上一章节一样。只是登录页面和授权页面改变了。这里就不进行测试
项目源码

https://gitee.com/lvhaibao/spring-lhbauth/tree/9a99e050b685df563825493bff5a4694e1f12eaf/