【Shiro免密登录集成钉钉扫码登陆功能实现】

一、需求背景

最近公司来个新需求需要加一个钉钉扫码自动登录的功能,考虑到公司目前权限使用的框架是Shiro,开发过程中存在部分问题记录一下。

钉钉开放平台链接:钉钉开放平台

二、步骤

1.引入依赖

代码如下:

<dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.10</version>
  </dependency>
  <dependency>
      <groupId>com.alibaba.openplatform.shared</groupId>
      <artifactId>sdk.client</artifactId>
      <version>1.2.0</version>
  </dependency>

备注:需要提前下载钉钉开发文档中的SDK(sdk.client)上传到公司的私服中。
下载地址:SDK下载地址
joda-time如果导入的话会报错。

2.熟悉扫码登录流程

钉钉开放平台扫码登录流程

3.获取appKey和appSecret

获取方式:获取appKey和appSecret

4.获取 access_token

获取方式:获取access_token

获取AccessToken我这里使用的是定时任务来获取每两小时获取一次,防止造成系统资源浪费。

    /**
     * 每两小时获取一次token存放在redis中
     */
    @Scheduled(cron = "0 2 0/2 * * ?")
    private void syncDingAccessToken(){
        String accessToken = dingUtils.getAccessToken();
        redisTemplate.opsForValue().set(redisKeyPrefix+dataCache+RedisKeyConstants.DING_ACCESS_TOKEN,accessToken,7200, TimeUnit.SECONDS);
    }
 	@Resource
    private DingConfigProperties properties;
/**
     * 初始化Client
     * @return
     */
    private ExecutableClient initClient(){
    	//properties是@ConfigurationProperties(prefix = "ding")注解实现的配置绑定类,可以自己		//实现下。除非你是注解驱动开发小白。。
        ExecutableClient executableClient = ExecutableClient.getInstance();
        executableClient.setAccessKey(properties.getAppKey());
        executableClient.setSecretKey(properties.getAppSecret());
        executableClient.setDomainName(properties.getGetTokenDomain());
        executableClient.setProtocal(properties.getProtocal());
        executableClient.init();
        //executableClient要单例,并且使用前要初始化,只需要初始化一次
        return executableClient;
    }
    /**
     * 获取accessToken
     */
    public String getAccessToken() {
        ExecutableClient executableClient = initClient();
        //获取token的配置地址
        String api = properties.getGetTokenApi();
        PostClient postClient = executableClient.newPostClient(api);
        String result = postClient.post();
        log.debug("accessToken:{}",result);

        JSONObject resultObject = JSONObject.parseObject(result);
        JSONObject content = resultObject.getJSONObject("content");
        if(!"0".equals(content.getString("responseCode"))){
            throw new BusinessException("登录失败,未正常获取token");
        }
        return content.getJSONObject("data").getString("accessToken");
    }

5.构建扫码登录界面

Web系统可以通过两种方式实现政务钉钉扫码登录。

方式一 使用政务钉钉提供的扫码登录页面

在企业Web系统里,用户点击使用钉钉扫码登录,第三方Web系统跳转到如下地址:

https://login.dg-work.cn/oauth2/auth.htm?response_type=code&client_id=应用标识&redirect_uri=回调地址&scope=get_user_info&authType=QRCODE

URL中的client_id和redirect_uri两个参数的值填入第三方web系统的应用标识和回调地址。政务钉钉用户扫码登录并确认后,会302到你指定的redirect_uri,并向url参数中追加临时授权码code(此code非authcode)及state两个参数。

注意事项:
参数"redirect_uri=回调地址"涉及的域名,需和创建扫码登录应用授权时填写的回调域名一致,否则会提示无权限访问。

生成二维码大小固定为200*200px,不支持修改。

方式二 支持网站将政务钉钉登录二维码内嵌到自己页面中

步骤1:在页面中通过iframe嵌入页面

通过方式一构造的地址增加embedMode=true的参数

https://login.dg-work.cn/oauth2/auth.htm?response_type=code&client_id=应用标识&redirect_uri=回调地址&scope=get_user_info&authType=QRCODE&embedMode=true

步骤2:扫码成功后需要在页面中监听扫码结果

<script type="application/javascript">
    window.addEventListener('message', function(event) {
                    // 这里的event.data 就是登录成功的信息
                          // 数据格式:{ "code": "aaaa", "state": "bbbb" }
        alert(JSON.stringify(event.data));
    });
</script>

6.通过钉钉获取登录用户信息

 /**
     * 获取用户信息
     * code:临时授权码。上一步获取的,主要是前端传给你。后端开发不用关心这个从哪里来。
     */
    public JSONObject getUserInfo(String code,String accessToken){
        ExecutableClient executableClient = initClient();
        String api = properties.getGetUserInfoApi();
        PostClient postClient = executableClient.newPostClient(api);
        postClient.addParameter("access_token",accessToken);
        postClient.addParameter("code",code);
        String result = postClient.post();
        log.debug("userInfo:{}",result);
        JSONObject resultObject = JSONObject.parseObject(result);
        JSONObject content = resultObject.getJSONObject("content");
        if(!"0".equals(content.getString("responseCode"))){
            throw new BusinessException(content.getString("responseMessage"));
        }

        return content;
    }

7.Shiro免密登录功能配置

新建自定义UserNamePasswordToken类,继承UsernamePasswordToken

/**
 * @类描述:钉钉扫码登录实现免密登录自定义类
 * @Author:
 */
public class UserNamePasswordToken extends UsernamePasswordToken {
	//用于判断是否需要免密登录
    private boolean pwdCheck = true;

    public void setPwdCheck(boolean pwdCheck) {
        this.pwdCheck = pwdCheck;
    }

    public boolean getPwdCheck() {
        return this.pwdCheck;
    }
	
	//构造方法调用父类初始化方法
    public UserNamePasswordToken(String username,String password){
        super(username,password);
    }
    public UserNamePasswordToken(String username){
        super(username,"");
    }
}

新建自定义密码匹配器类,集成HashedCredentialsMatcher类

/**
 * 自定义密码认证匹配
 */
public class SysCredentialsMatch extends HashedCredentialsMatcher {
    public SysCredentialsMatch() {
        super();
    }

    public SysCredentialsMatch(String hashAlgorithmName) {
        super(hashAlgorithmName);
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UserNamePasswordToken userNamePasswordToken = (UserNamePasswordToken) token;
        //如果免密登录则直接返回true
        if(!userNamePasswordToken.getPwdCheck()){
            return true;
        }
        //否则按照之前验证密码
        return super.doCredentialsMatch(token, info);
    }
}

最后将Bean放到Spring容器中

 @Bean
 public SysCredentialsMatch customCredentialsMatch() {
 //根据自身项目情况配置,不可完全复制
     SysCredentialsMatch customCredentialsMatch = new SysCredentialsMatch();
     customCredentialsMatch.setHashAlgorithmName("md5");
     //此处配置修改需注意,会导致密码验证失败
     customCredentialsMatch.setHashIterations(1024);
     customCredentialsMatch.setStoredCredentialsHexEncoded(true);
     return customCredentialsMatch;
 }

 @Bean
 public MyShiroRealm myShiroRealm(){
     MyShiroRealm myShiroRealm = new MyShiroRealm();
    //在自定义Realm中使用自定义的密码匹配器customCredentialsMatch
     myShiroRealm.setCredentialsMatcher(customCredentialsMatch());
     return myShiroRealm;
 }

然后登录钉钉返回的用户信息在自己系统中进行判断,是否允许登录即可。
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 中使用 Shiro 实现免密登录的步骤如下: 1. 导入 ShiroSpring Boot 的相关依赖。在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建一个自定义的 ShiroRealm 类,并继承 org.apache.shiro.realm.AuthenticatingRealm 类。在该类中重写 doGetAuthenticationInfo 方法,用于验证用户身份信息。这里可以通过用户名和密码进行验证,也可以通过其他方式实现免密登录。 3. 在 application.properties 或 application.yml 文件中配置 Shiro 的相关属性。例如,配置 Shiro 使用的 Realm 类和登录 URL: ```yaml shiro: authc: loginUrl: /login realms: myRealm: authenticationTokenClass: org.apache.shiro.authc.UsernamePasswordToken ``` 4. 在 Spring Boot 的入口类上添加 @EnableShiro 注解,启用 Shiro 功能。 5. 创建一个登录接口,处理用户登录请求。在该接口中,可以使用 Shiro 的 Subject 类来进行登录验证。例如: ```java @PostMapping("/login") public String login(String username, String password) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); return "登录成功"; } catch (AuthenticationException e) { return "用户名或密码错误"; } } ``` 这样,当用户访问登录接口并提供正确的用户名和密码时,就可以实现免密登录,并返回登录成功的提示信息。当用户提供错误的用户名或密码时,将返回错误提示信息。 注意:以上只是简单示例,实际应用中可能需要更复杂的逻辑和安全措施。建议参考 Shiro 的官方文档和示例代码来进行具体的实现和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值