美文网首页框架建设收集
SpringSecurity 个性用户认证流程

SpringSecurity 个性用户认证流程

作者: 青衣敖王侯 | 来源:发表于2019-06-07 14:23 被阅读28次

   上一篇文章中我们讲了Spring Security自定义用户认证逻辑。本篇文章将在上一篇文章的基础上做一些个性化用户的认证流程。所涉及的个性化用户认证流程有:

  • 自定义登录页面
    上一篇文章中我们用的登录表单依然是springsecurity的默认登录页面
  • 自定义登录成功处理
    有时候我们希望登录成功处理后不只是简单跳转到首页,我们还希望给用户颁发优惠券等等
  • 自定义登录失败处理
    当用户登录失败后,我们有时候也希望记录日志等信息,而不是像上一章节中直接弹出登录错误的提示。

1.自定义登录页面实战

1.1定义前端页面

siginIn.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
    <h2>标准登录页面</h2>
    <h3>表单登录</h3>
    <form action="/authentication/form" method="post">
        <table>
            <tr>
                <td>用户名:</td> 
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2"><button type="submit">登录</button></td>
            </tr>
        </table>
    </form>
</body>
</html>

这里的登录页面只有用户名和密码加上一个登录按钮,然后页面名字为signIn.html

1.2添加配置

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().loginPage("/signIn.html")
        .loginProcessingUrl("/authentication/form")
                // http.httpBasic()
                .and().authorizeRequests()// 表示下面是认证的配置
                .antMatchers("/signIn.html").permitAll()
                .anyRequest()// 任何请求
                .authenticated()// 都需要身份认证
        .and()
        .csrf().disable();
    }

  为了能够使用我们的自定义页面,这里我们添加了loginPage("/signIn.html")。表单填写完成提交后,会被UsernamePasswordAuthenticationFilter拦截并验证用户名和密码,但是这个拦截器默认只拦截/login的请求路径,为了拦截我们提交的表单请求,所以要和signIn.html提交的/authentication/form路径一样。antMatchers("/signIn.html").permitAll()表示登录页面不需要认证,否则页面会不断重定向到自定义登录页面。csrf().disable()的添加是因为SpringSecurity默认会开启csrf安全机制,这里我们先关闭一下,否则我们会请求失败。

1.3总共需要添加的内容

1.4结果展示


  OK,自定义登录页面的demo我们就成功搭建好了。
  但是。。。
  大家有没有发现。。。
  2个问题。。。
  第一个问题:我们如果发出去的是rest请求,返回的却是一个.html,而不是一个json应答。这有点违背restful的风格,因此我们要做一个改造。如果我们发起的是rest请求,就得到json应答,如果发起的是html请求,就跳转到登录页面。
  第二个问题:我们的browser模块、app模块、core模块都是提供给外部服务的,引用我们模块的用户有时候希望定义自己的登录表单,而不是我们上面提供的默认的自定义表单。
我们的改造思路如下:


改造思路

2.改造

2.1在browser模块中自定义Controller

@RestController
public class BrowserSecurityController {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private RequestCache requestCache = new HttpSessionRequestCache();
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    @Autowired
    private SecurityProperties securityProperties;

    @RequestMapping("/authentication/require")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            logger.info("引发跳转的请求是:" + targetUrl);
            if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
            }
        }
        return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页面");
    }
}

  这个Controller的requestMapping是/authentication/require,我们之前配置的loginPage和antMathcers都必须有这个路径,这样用户没有登录的时候才会跳转到这个Controller里面来。
  RequestCache用于获取引发跳转的请求路径,比如我们在浏览器中访问localhost:8080/index.html,此时SpringSecurity检测到我们没有登录则会跳转到这个Controller中,如果直接用request.getRequestURI看到的一定是/autheication/require,如下图所示:

image.png
  为了知道用户是访问的是HTML还是rest接口,所以使用SpringSecurity提供的HttpSessionRequestCache。
  这个Controller中如果发现请求的html,则会使用SpringSecurity的重定向策略DefaultRedirectStrategy,跳转到用户的配置页面redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());这里的securityProperties封装了所有的安全配置,包含浏览器的配置,OAuth等等配置,一会儿我们将在core模块中讲到他。如果用户请求的不是html,最后会返回一个json对象SimpleResponse给用户,且应答码是401,如果我们没有登录直接访问http://localhost:8080/user,结果如下图所示:

2.2core模块的改造

在2.1中我们看到SecurityProperties,这是因为我们所编写的模块是要供用户使用,为了提供可重用的模块,让使用我们core和browser、app模块的用户能够自定义自己的登录页面,所以我们将在core中编写一下这个SecurityProperties。如下图所示:


SecurityProperties

2.2.1 SecurityProperties

@ConfigurationProperties(prefix = "imooc.security")
public class SecurityProperties {
    private BrowserProperties browser = new BrowserProperties();

    public BrowserProperties getBrowser() {
        return browser;
    }

    public void setBrowser(BrowserProperties browser) {
        this.browser = browser;
    }

}

2.2.2 BrowserProperties

public class BrowserProperties {
    private String loginPage="/signIn.html";

    public String getLoginPage() {
        return loginPage;
    }

    public void setLoginPage(String loginPage) {
        this.loginPage = loginPage;
    }
    
}

我们的BrowserProperties就只有一个loginPage,默认值就是用户没有自定义登录页面时候使用的browser提供的默认自定义页面。

2.2.3SecurityCoreConfig

@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {

}

@EnableConfigurationProperties注解的作用是:让使用 @ConfigurationProperties 注解的类生效。

2.3 demo自定义页面

2.3.1application.properties增加loginPage配置

imooc.security.browser.loginPage=/demo-signIn.html

2.3.2增加自定义页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
    <h2>Demo登录页</h2>
</body>
</html>

2.4BrowserSecurityConfig配置改造

BrowserSecurityConfig

logingPage变成了controller的访问路径,antMatchers,除了controller的路径还有就是用户自定义的登录页面的路径。

2.5验证

自定义登录页面

我们访问时跳转到了我们自定义的登录页面,如果我们把2.3.1application.properties的配置去掉,看到的就是如下的默认登录页面:


默认登录页面

3.自定义登录成功处理

3.1AuthenticationSuccessHandler

SpringSecurity提供了一个AuthenticationSuccessHandler接口,当用户认证成功后,会调用这个接口中唯一的方法onAuthenticationSuccess,所以我们只要重写这个方法就可以自定义我们登录成功的逻辑

public interface AuthenticationSuccessHandler {

    /**
     * Called when a user has been successfully authenticated.
     *
     * @param request the request which caused the successful authentication
     * @param response the response
     * @param authentication the <tt>Authentication</tt> object which was created during
     * the authentication process.
     */
    void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException;

}

其中的Authentication包含了登录成功后的用户的详细信息,这个也是我们重点要获取的对象
自定义Handler:

@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperties securityProperties;

    private RequestCache requestCache = new HttpSessionRequestCache();

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.security.web.authentication.
     * AuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.
     * HttpServletRequest, javax.servlet.http.HttpServletResponse,
     * org.springframework.security.core.Authentication)
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {

        logger.info("登录成功");

        if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginResponseType())) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        } else {
            //调用父类的方法跳转
            super.onAuthenticationSuccess(request, response, authentication);
        }

    }

}

如果用户配置以JSON的方式响应,那么我们这里暂且返回authentication中的信息,如果是重定向的方式,我们就调用父类默认的跳转方法,这个父类SavedRequestAwareAuthenticationSuccessHandler实现了AuthenticationSuccessHandler接口

3.2BrowserSecurityConfig配置

BrowserSecurityConfig配置

配置好登录成功要使用的handler。

3.3core模块BrowserProperties编写

3.4访问验证


我们来分析一下返回的json串,admin表示Userdetails里面返回的权限。principal里面是最最关键的内容,返回了用户的用户名和密码,不过密码默认被springSecurity设置为null了,authorities里面的信息就是Userdetails里面的信息。

4.自定义登录失败处理

4.1AuthenticationFailureHandler

用户认证失败时也有一个类似AuthenticationSuccessHandler的Handler,它就是AuthenticationFailureHandler

public interface AuthenticationFailureHandler {

    /**
     * Called when an authentication attempt fails.
     * @param request the request during which the authentication attempt occurred.
     * @param response the response.
     * @param exception the exception which was thrown to reject the authentication
     * request.
     */
    void onAuthenticationFailure(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException;
}

自定义是失败的Handler

@Component("imoocAuthenctiationFailureHandler")
public class ImoocAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperties securityProperties;

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.security.web.authentication.AuthenticationFailureHandler#
     * onAuthenticationFailure(javax.servlet.http.HttpServletRequest,
     * javax.servlet.http.HttpServletResponse,
     * org.springframework.security.core.AuthenticationException)
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {

        logger.info("登录失败");

        if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginResponseType())) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        } else {
            super.onAuthenticationFailure(request, response, exception);
        }

    }
}

AuthenticationException是用户登录失败后记录的具体异常信息

4.2BrowserSecurityConfig配置

4.3输入错误密码验证

相关文章

网友评论

    本文标题:SpringSecurity 个性用户认证流程

    本文链接:https://www.haomeiwen.com/subject/blhmxctx.html