欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Security(三)

程序员文章站 2022-04-18 15:27:19
Spring Security(三) 个性化用户认证流程 自定义登录页面 在配置类中指定登录页面和接收登录的 url 在项目中新建登录页面 启动项目时再访问 Security 就会跳转到你自已定义的登陆页面让你登录。 深入定义(判断是PC端还是移动端,PC端跳转页面,移动端响应 json) 创建一个 ......

spring security(三)

个性化用户认证流程

自定义登录页面

在配置类中指定登录页面和接收登录的 url

@configuration
public class browsersecurityconfig extends websecurityconfigureradapter {

    @bean
    public passwordencoder passwordencoder() {
        return new mypasswordencoder();
    }

    @override
    protected void configure(httpsecurity http) throws exception {
        // 启用表单登陆
        http.formlogin()
                // 指定登录页面
                .loginpage("/imooc-signin.html")
                // 登录页面表单提交的 action
                .loginprocessingurl("/authentication/form")
                .and()
                // 对请求做授权
                .authorizerequests()
                // 访问指定url时不需要身份认证(放行)
                .antmatchers("/imooc-signin.html").permitall()
                // 任何请求
                .anyrequest()
                // 都需要身份认证
                .authenticated()
                .and()
                .csrf().disable();
    }
}
  • 在项目中新建登录页面
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>登录</title>
</head>
<body>
    <h2>标准登录页面</h2>
    <h3>表单登录</h3>
    <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>

启动项目时再访问 security 就会跳转到你自已定义的登陆页面让你登录。

Spring Security(三)

  • 深入定义(判断是pc端还是移动端,pc端跳转页面,移动端响应 json)

创建一个控制器,用来处理操作

@restcontroller
public class browsersecuritycontroller {

    private static final logger log = loggerfactory.getlogger(browsersecuritycontroller.class);

    private requestcache requestcache = new httpsessionrequestcache();

    private redirectstrategy redirectstrategy = new defaultredirectstrategy();

    /**
     * 当需要身份验证时跳转到这里处理
     */
    @requestmapping("/authentication/require")
    @responsestatus(code = httpstatus.unauthorized)
    public map<string, string> requireauthentication(final httpservletrequest request,
                                                final httpservletresponse response) throws ioexception {
        savedrequest savedrequest = requestcache.getrequest(request, response);
        if (null != savedrequest) {
            string target = savedrequest.getredirecturl();
            log.info("引发跳转的请求是 ={}", target);
            if (stringutils.endswithignorecase(target, ".html")) {
                redirectstrategy.sendredirect(request, response, "/imooc-signin.html");
            }
        }
        map<string, string> map = new hashmap<>();
        map.put("status", "401");
        map.put("msg", "error");
        map.put("content","访问的服务需要身份认证,请引导用户到登录页!" );
        return map;
    }
}
自定义登录成功处理

要做自定义登录成功处理需要实现一下 security 的 authenticationsuccesshandler 接口

/**
 * 自定义登录成功处理
 */
@configuration
public class imoocauthenticationsuccesshandler implements authenticationsuccesshandler {
    private static final logger log = loggerfactory.getlogger(imoocauthenticationsuccesshandler.class);

    private final objectmapper objectmapper;

    @autowired
    public imoocauthenticationsuccesshandler(objectmapper objectmapper) {
        this.objectmapper = objectmapper;
    }
    // 参数 authentication 是security的核心接口之一,封装了用户登录认证信息
    // userdetails 接口就包装到了此接口中
    @override
    public void onauthenticationsuccess(httpservletrequest request,
                                        httpservletresponse response,
                                        authentication authentication) throws ioexception, servletexception {
        log.info("登录成功");
        // 响应 json 信息
        response.setcontenttype("application/json;charset=utf-8");
        printwriter out = response.getwriter();
        out.write(objectmapper.writevalueasstring(authentication));
        out.flush();
        out.close();
    }

}
  • 启动项目,访问跳转登录页面后,输入正确用户名,密码后响应信息如下:
{
  "authorities": [
    {
      "authority": "admin"
    }
  ],
  // 包含了认证请求的信息
  "details": {
    "remoteaddress": "127.0.0.1",
    "sessionid": "1126c43793fd600ca6dc74169a38f64e"
  },
  // 这里代表当前用户是否经过了身份认证,boolean表示
  "authenticated": true,
  // 这里是 我们自定义userdetailsservice接口实现类 返回的数据
  "principal": {
    "password": null,
    "username": "user",
    // 用户权限
    "authorities": [
      {
        "authority": "admin"
      }
    ],
    "accountnonexpired": true,
    "accountnonlocked": true,
    "credentialsnonexpired": true,
    "enabled": true
  },
  // 这里一般代表用户输入的密码,security 做了处理,前台不会响应
  "credentials": null,
  // 用户名
  "name": "user"
}
自定义登录错误处理

要做自定义登录成功处理需要实现一下 security 的 authenticationfailurehandler 接口

/**
 * 自定义登录失败处理
 */
@configuration
public class imoocauthenticationfailurehandler implements authenticationfailurehandler {
    // 参数 authenticationexception 是 security 的一个抽象异常类
    @override
    public void onauthenticationfailure(httpservletrequest request,
                                        httpservletresponse response,
                                        authenticationexception exception) throws ioexception, servletexception {

    }

}
  • 启动项目,访问跳转登录页面后,输入错误用户名,密码后响应信息如下:
{**省略堆栈信息**"localizedmessage":"坏的凭证","message":"坏的凭证","suppressed":[]}

注意:以上两个自定义登录/失败的处理,一定要在 自定义security配置类中加入,不然不会生效!!!

加入自定义登录成功/失败处理

/**
 * security 配置
 */
@configuration
public class browsersecurityconfig extends websecurityconfigureradapter {

    @autowired
    private securityproperties securityproperties;

    @autowired
    private imoocauthenticationsuccesshandler imoocauthenticationsuccesshandler;

    @autowired
    private imoocauthenticationfailurehandler imoocauthenticationfailurehandler;

    @bean
    public passwordencoder passwordencoder() {
        return new mypasswordencoder();
    }

    @override
    protected void configure(httpsecurity http) throws exception {
        browserproperties browser = securityproperties.getbrowser();
        // 启用表单登陆
        http.formlogin()
                // 指定登录页面
                .loginpage("/authentication/require")
                // 登录页面表单提交的 action
                .loginprocessingurl("/authentication/form")
                // 引入自己定义的登录成功处理配置类
                .successhandler(imoocauthenticationsuccesshandler)
                // 引入自己定义的登录失败处理配置类
                .failurehandler(imoocauthenticationfailurehandler)
                .and()
                // 对请求做授权
                .authorizerequests()
                // 访问指定url时不需要身份认证(放行)
                .antmatchers("/authentication/require",
                        browser.getloginpage()).permitall()
                // 任何请求
                .anyrequest()
                // 都需要身份认证
                .authenticated()
                .and()
                .csrf().disable();

    }
}

以上实现了自定义成功/失败响应,但是要想pc/移动端通用,需要深化配置一下

  • 改造 自定义成功处理

创建一个枚举类,用来区分 重定向,还是响应 json

public enum logintype {

    redirect, json;

}

让此枚举类成为 browserproperties 类的一个属性

public class browserproperties {

    /**
     * 指定默认值(如果配置了用配置的页面,没配置用默认的。)
     */
    private string loginpage = "/imooc-signin.html";

    /**
     * 指定登录成功/失败后的响应方式
     */
    private logintype logintype = logintype.json;
    
    // 省略 get/set/tostring 方法
}

改造自定义成功处理类

/**
 * 自定义登录成功处理
 */
@configuration
// 让自定义的成功处理类 继承 security 默认的成功处理类 savedrequestawareauthenticationsuccesshandler
public class imoocauthenticationsuccesshandler extends savedrequestawareauthenticationsuccesshandler {

    private static final logger log = loggerfactory.getlogger(imoocauthenticationsuccesshandler.class);

    private final objectmapper objectmapper;

    private final securityproperties securityproperties;

    @autowired
    public imoocauthenticationsuccesshandler(objectmapper om, securityproperties sp) {
        this.objectmapper = om;
        this.securityproperties = sp;
    }

    @override
    public void onauthenticationsuccess(httpservletrequest request,
                                        httpservletresponse response,
                                        authentication authentication) throws ioexception, servletexception {
        log.info("登录成功。。。");
        // 响应 json 信息
        response.setcontenttype("application/json;charset=utf-8");
        printwriter out = response.getwriter();
        out.write(objectmapper.writevalueasstring(authentication));
        out.flush();
        out.close();
    }

}

改造自定义失败处理类

/**
 * 自定义登录失败处理
 */
@configuration
// simpleurlauthenticationfailurehandler 为 security 默认的登录失败处理类
public class imoocauthenticationfailurehandler extends simpleurlauthenticationfailurehandler {

    private static final logger log = loggerfactory.getlogger(imoocauthenticationsuccesshandler.class);

    private final objectmapper objectmapper;

    private final securityproperties securityproperties;

    @autowired
    public imoocauthenticationfailurehandler(objectmapper om, securityproperties sp) {
        this.objectmapper = om;
        this.securityproperties = sp;
    }

    @override
    public void onauthenticationfailure(httpservletrequest request,
                                        httpservletresponse response,
                                        authenticationexception exception) throws ioexception, servletexception {
        log.info("登录失败。。。");
        // 如果配置了用 json 响应
        if (logintype.json.equals(securityproperties.getbrowser().getlogintype())) {
            // 响应状态码为 500
            response.setstatus(httpstatus.internal_server_error.value());
            // 响应 json 信息
            response.setcontenttype("application/json;charset=utf-8");
            printwriter out = response.getwriter();
            out.write(objectmapper.writevalueasstring(exception));
            out.flush();
            out.close();
        } else {
            // 调用父类方法
            super.onauthenticationfailure(request, response, exception);
        }
    }
}

由于 browserproperties 类中的logintype属性默认为 json ,你可以在具体的 properties 文件中,定义一下属性。如:

imooc.security.browser.login-type=redirect

这样可以测试一下是否配置成功。

测试图就不贴上了,自已耐心测试一下~ :-)