在前面的文章中,我们自定义了一些CA登录相关的类,如 CertificateAuthorityAuthenticationTokenCertificateAuthorityAuthenticationFilterCertificateAuthorityDaoAuthenticationProvider等。但是,由于诸多条件的不具备,也就没有办法演示。

不过,目前一个新登录方式所需要的逻辑基本都介绍过了,下面,就利用这些已经自定义的类,来尝试如何自定义个新的登录方式-CA登录。并且,CA登录需要与默认的用户名密码登录方式共存,也即用户可以自由选择何种登录方式来登录系统。

 

登录页面改造

 

首先,我们来改造一下登录页面,添加CA登录方式。

<div class="login-form">
  <ul class="nav nav-tabs mb-3" role="tablist">
    <li class="nav-item">
      <a class="nav-link active" href="#login" data-toggle="tab">用户登录</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#ca_login" data-toggle="tab">CA登录</a>
    </li>
  </ul>
  <div class="tab-content">
    <div id="login" class="tab-pane active">
      <form th:action="@{/login}" method="post" th:method="post" class="mt-1">
        <div class="form-group">
          <input type="text" class="form-control" name="username" placeholder="用户名">
        </div>
        <div class="form-group">
          <input type="password" class="form-control" name="password" placeholder="密码">
        </div>
        <div class="checkbox">
          <label><input type="checkbox"> 记住我</label>
        </div>
        <button type="submit" class="btn btn-primary btn-block mb-1 mt-1">登录</button>
        <p class="text-muted text-center"> <a href="login.html#">
          <small>忘记密码了?</small></a> | <a href="#">注册一个新账号</a>
        </p>
      </form>
    </div>
    <div id="ca_login" class="tab-pane">
      <form th:action="@{/certificate_authority_login}" method="post" th:method="post" class="mt-1">
        <div class="form-group">
          <input type="text" class="form-control" name="signature" value="3691308F2A4C2F6983F2880D32E29C84" readonly />
        </div>
        <button type="submit" class="btn btn-primary btn-block mb-1 mt-1">登录</button>
        <p class="text-muted text-center"> <a href="login.html#">
          <small>忘记密码了?</small></a> | <a href="#">注册一个新账号</a>
        </p>
      </form>
    </div>
  </div>
</div>

页面效果如下:

springsecurity 密码错误重试次数 springsecurity默认用户名密码_User

 

使用CA签名获取用户信息

 

首先,需要修改 SYS_USER 表结构,新增 signature 字段,即CA签名,当然,也可以是能标识CA唯一性的其它属性。

alter table SYS_USER add signature varchar(60) comment 'ca签名';

接下来,把之前用户名为 zhangsan 的数据,添加 signature 字段值。

UPDATE sys_user SET signature = '3691308F2A4C2F6983F2880D32E29C84' WHERE ID = '2031a4adc78942d59188cea7927e6304';

然后,UserDao 新增使用 signature 查询用户信息的方法。

public SysUser getBySignature(String signature) {
    String sql = "select id, name, username, password, age, state from sys_user where signature = :signature";

    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("signature", signature);

    return get(sql, paramMap, SysUser.class);
}

最后,自定义 UserDetailsService 接口实现 CertificateAuthorityJdbcUserDetailsService,以注入 UserDao 查询用户信息。

public UserDetails loadUserByUsername(String signature) throws UsernameNotFoundException {
    SysUser sysUser = this.userDao.getBySignature(signature);

    User.UserBuilder builder = User.builder()
        .username(sysUser.getUsername())
        .password(sysUser.getPassword());

    List<String> roles = this.roleDao.listRoleCodeByUserId(sysUser.getId());

    builder.roles(roles == null ? new String[] {} : roles.toArray(new String[] {}));

    return builder.build();
}

 

Spring Security配置改造

 

最后,把CA登录相关的配置添加到 Spring Security 配置中即可。

@EnableWebSecurity
@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

   ......

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(certificateAuthorityDaoAuthenticationProvider())
                .userDetailsService(customJdbcUserDetailsService())
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    private AbstractAuthenticationProcessingFilter certificateAuthorityAuthenticationFilter() throws Exception {
        CertificateAuthorityAuthenticationFilter authorityAuthenticationFilter = new CertificateAuthorityAuthenticationFilter();
        authorityAuthenticationFilter.setAuthenticationSuccessHandler(certificateAuthorityAuthenticationSuccessHandler());
        authorityAuthenticationFilter.setAuthenticationFailureHandler(certificateAuthorityAuthenticationFailureHandler());
        authorityAuthenticationFilter.setAuthenticationManager(authenticationManager());
        return authorityAuthenticationFilter;
    }

    private AuthenticationProvider certificateAuthorityDaoAuthenticationProvider() {
        CertificateAuthorityDaoAuthenticationProvider certificateAuthorityDaoAuthenticationProvider = new CertificateAuthorityDaoAuthenticationProvider();
        certificateAuthorityDaoAuthenticationProvider.setUserDetailsService(certificateAuthorityJdbcUserDetailsService());
        return certificateAuthorityDaoAuthenticationProvider;
    }

    private UserDetailsService certificateAuthorityJdbcUserDetailsService() {
        CertificateAuthorityJdbcUserDetailsService userDetailsService = new CertificateAuthorityJdbcUserDetailsService();
        userDetailsService.setUserDao(userDao);
        userDetailsService.setRoleDao(roleDao);
        return userDetailsService;
    }

    private AuthenticationSuccessHandler certificateAuthorityAuthenticationSuccessHandler() {
        SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        authenticationSuccessHandler.setDefaultTargetUrl("/index");
        return authenticationSuccessHandler;
    }

    private AuthenticationFailureHandler certificateAuthorityAuthenticationFailureHandler() {
        SimpleUrlAuthenticationFailureHandler authenticationFailureHandler = new SimpleUrlAuthenticationFailureHandler();
        authenticationFailureHandler.setDefaultFailureUrl("/login_fail");
        return authenticationFailureHandler;
    }

  ......

}

 

登录尝试

 

首先,我们尝试CA登录方式。点击登录页面的CA登录TAB页,切换到CA登录方式,此时,CA的相关信息可正常显示到页面中(由CA厂商提供的API从插入电脑USB口的CA Key中获取)。

springsecurity 密码错误重试次数 springsecurity默认用户名密码_bc_02

然后,点击 登录 按钮(注意,真实情况下,需要输入CA Key的密码,此处省略)系统正常登录。

springsecurity 密码错误重试次数 springsecurity默认用户名密码_bc_03

查看个人中心,用户正确无误。

springsecurity 密码错误重试次数 springsecurity默认用户名密码_User_04

接下来,退出登录,切换到用户登录TAB页,使用用户名密码方式再次登录,同样可以正常登录系统,并无影响。两种登录方式完美共存。

CA登录方式实现及配置完成。