shiro+微信登录整合

(1)加密工具的抽取

import org.apache.shiro.crypto.hash.SimpleHash;
public class MD5Util {
    //加密方式
    public static final String ALGORITHMNAME = "MD5";
    //盐值
    public static final String SALT  ="source";
    //加密次数
    public static final int HASHITERATIONS  = 10;
    //把密码传进来进行加密
    public static String  createMD5(String password){
        SimpleHash hash = new SimpleHash(ALGORITHMNAME,password,SALT,HASHITERATIONS);
        return hash.toString();
    }
}

(2)自定义一个类实现AuthorizingRealm,它有两个方法,doGetAuthenticationInfo身份验证和doGetAuthorizationInfo授权
我们需要从写里面的逻辑

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private IEmployeeService employeeService ;
    @Override//授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    @Override//身份认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //判断当前登录用户账号是否存在
        String username = (String)token.getPrincipal();
//从数据根据名字取查询有没有这个人物
        Employee employee  =employeeService.findByUsername(username);
        if(employee==null){
            return null ;
        }
        //判断密码是否正确
        //加盐
        ByteSource source = ByteSource.Util.bytes(MD5Util.SALT);
        //第一个参数用户的身份,第二个是数据库查询的密码,第三个加盐,第四个当前realm的名字
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(employee, employee.getPassword(), source, getName());
        return info ;
    }
}

(3)applicationContext-shiro的配置,注意在applicationContext中引用这个配置,还有shiro的监听器要和web.xml中配置的名字相同

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>
    <bean id="myRealm" class="cn.yhh.realm.MyRealm">
        <property name="name" value="myRealm"/>
        <!-- 配置密码匹配器 -->
        <property name="credentialsMatcher">
//自定义的验证器,第三方登录中需要跳过密码的验证
            <bean class="cn.yhh.realm.RetryLimitHashedCredentialsMatcher">
                <!--认证方式-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--加密次数-->
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--如果没有认证,都跳到loginUrl对应的路径-->
        <property name="loginUrl" value="/login"/>
        <!--如果认证通过之后,就跳到successUrl对应的路径-->
        <property name="successUrl" value="/s/success.jsp"/>
        <!--如果你访问某个资源,没有权限,就跳到unauthorizedUrl对应的路径中-->
        <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = anon
                /code.html = anon
                /logout = logout
                /wechat/callback=anon//微信跳转需要的路径
                /static/** = anon
                /** = user //表示携带User对象的用户可以访问所有资源
            </value>
        </property>
    </bean>
</beans>

4)微信登录

  • 因为三方登录是不要进行密码的验证的,所有我们自定义一个类去继承HashedCredentialsMatcher(在shiro.xml中配置),我们只需要用户授权就可以,不需要进行验证密码,
    但是我们用shiro 登录需要拿到一个token的令牌,所以我们又要自定义一个类去覆写UsernamePasswordToken,为了方便可以定义一个枚举表示当前的登录类型
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    @Override
    public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
        EasyTypeToken tk = (EasyTypeToken) authcToken;
        //如果是免密登录直接返回true
        if(tk.getType().equals(LoginType.NOPASSWD)){
            return true;
        }
        //不是免密登录,调用父类的方法
       return super.doCredentialsMatch(tk, info);
    }
}
import org.apache.shiro.authc.UsernamePasswordToken;
public class EasyTypeToken extends UsernamePasswordToken {
    //这随便写的吧应该都可以
    private static final long serialVersionUID = -2564928913725078138L;
    private LoginType type;//表示当前登录的类型
    public EasyTypeToken() {
        super();
    }
    public EasyTypeToken(String username, String password, LoginType type, boolean rememberMe,  String host) {
        super(username, password, rememberMe,  host);
        this.type = type;
    }
    /**免密登录*/
    public EasyTypeToken(String username) {
        super(username, "", false, null);
        this.type = LoginType.NOPASSWD;
    }
    /**账号密码登录*/
    public EasyTypeToken(String username, String password) {
        super(username, password, false, null);
        this.type = LoginType.PASSWORD;
    }
    public LoginType getType() {
        return type;
    }
    public void setType(LoginType type) {
        this.type = type;
    }
}
public enum LoginType {
    PASSWORD("password"), // 密码登录
    NOPASSWD("nopassword"); // 免密登录
    private String code;// 状态值
    private LoginType(String code) {
        this.code = code;
    }
    public String getCode () {
        return code;
    }
}
  • 微信登录需要去微信卡发着平台申请拿到最主要的appid和secret
    在页面我们需要引入他的js文件
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
<!--定义一个容器装二维码-->
<div id="login_container">
</div>
</body>
<script>
    var obj = new WxLogin({
        self_redirect:false,
        id:"login_container",/*容器的id*/
        appid: "你自己申请的",
        scope: "snsapi_login",
        redirect_uri: "",//响应授权码的地址-第一部分是申请下来的地址,第二部分是跳转后台的路径
        state: "xxx",----状态,多用于判断当前登录的状态是否安全
        style: "white", ---二维码的颜色
        href: ""
    });
</script>
  • 导入jar包
<dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.47</version>
      </dependency>
      <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
      </dependency>

跳转后台的controller

import com.alibaba.fastjson.JSON;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;
@Controller
public class WxCharController {
    @Autowired
    private IEmployeeService employeeService ;
    @RequestMapping("/wechat/callback")
    public String callback(String code , String state){
        //这里可以判断state是否改变判断此次请求是否安全
        //得到code换取token的地址
        String getTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=你自己申请的&secret=和appid一样&code="+code+"&grant_type=authorization_code";
        //得到地址后通过工具类得到token
        String tokenResult = HttpClientUtil.doGet(getTokenUrl);
        System.out.println(tokenResult);
        //得到的token是json格式,将他转成map,得到换取用户资源的openid和access_token
        Map<String,String> tokenResultMap = JSON.parseObject(tokenResult,Map.class);
        //拿到token之后,换取用户资源
        String tokenString = tokenResultMap.get("access_token");
        String openId = tokenResultMap.get("openid");
        String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token="+tokenString+"&openid="+openId;
        //拿到了用户的基本信息
        String userResultStr = HttpClientUtil.doGet(userInfoUrl);
        System.out.println(userResultStr);
//根据opoenId去数据库获取user对象
        Employee employee  =employeeService.findByOpenid(openId);
        //如果user不为空 ,代码里面直接调用shiro的登录 -> 直接跳转主页
        if(employee!=null){
            String username = employee.getUsername();
	//这里用的就是我覆写的,并且通过自定义的拦截器,跳过密码的验证
            EasyTypeToken token = new EasyTypeToken(username);
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
//从session中拿到用户对象
            UserContext.setUserInSession(employee);
            return "main";

        }else {
            //如果user为空,说明这个openid没有绑定过,那么久要跳转到绑定页面
        }
        //绑定页面显示两种情况 : 绑定已有账号 ; 绑定新注册账号 ,把openId传入到页面
        //如果提交的是已有账号绑定 用户名 ,密码 ,openId
        //带着用户名和密码去数据库中查询 ,如果查到了User  ,就修改User.openId 设置进去 ,update.user
        //如果是提交注册 :用户名 ,密码 ,验证码 ,手机号  , openId
        //先做注册操作 insert.user  ,把openId和user信息一并保存
        return null ;

抽取通过地址获得token的工具

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientUtil {
    // http://bugtracker.itsource.cn/wechat/callback?code=222&state=99
    // http://bugtracker.itsource.cn/wechat/callback    code=222&state=99
    public static String doGet(String uri) {
        //1:创建一个HttpClient的实例
        CloseableHttpClient httpclient = HttpClients.createDefault();
        //2:创建一个get请求实例
        HttpGet httpGet = new HttpGet(uri);
        //请求的响应:
        CloseableHttpResponse response1 = null;
        try {
            //3:使用HttpClient的实例执行get请求
            response1 = httpclient.execute(httpGet);
            //http请求的状态:404 500 200
            System.out.println(response1.getStatusLine());
            int statusCode = response1.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                //请求成功:
                HttpEntity entity1 = response1.getEntity();
                String result = EntityUtils.toString(entity1, "utf-8");
                System.out.println(result);
                return result;
            } else {
                //请求失败:自己做自己的业务逻辑
                System.out.println("请求失败......:"+statusCode);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值