zoukankan      html  css  js  c++  java
  • 简单实现Shiro单点登录(自定义Token令牌)

    1. MVC Controller 映射 sso 方法。

    /**
     * 单点登录(如已经登录,则直接跳转)
     * @param userCode 登录用户编码
     * @param token 登录令牌,令牌组成:sso密钥+用户名+日期,进行md5加密,举例: 
     *         String secretKey = Global.getConfig("shiro.sso.secretKey");
     *         String token = Digests.md5(secretKey + userCode + DateUtils.getDate("yyyyMMdd"));
     * @param url 登录成功后跳转的url地址。
     * @param relogin 是否重新登录,需要重新登录传递true
     * 例如:http://localhost/project/sso/{token}?url=xxx&relogin=true
     */
    @RequestMapping(value = "sso/{userCode}/{token}")
    public String sso(@PathVariable String userCode, @PathVariable String token, 
            @RequestParam(required=true) String url, String relogin, Model model) {
        Principal principal = SecurityUtils.getSubject().getPrincipal();
        // 如果已经登录
        if(principal != null){
            // 如果设置强制重新登录,则重新登录
            if (BooleanUtils.toBoolean(relogin)){
                SecurityUtils.getSubject().logout();
            }
            // 否则,直接跳转到目标页
            else{
                return "redirect:" + Encodes.urlDecode2(url);
            }
        }
        // 进行单点登录
        if (token != null){
            UsernamePasswordToken upt = new UsernamePasswordToken();
            try {
                upt.setUsername(userCode); // 登录用户名
                upt.setPassword(token.toCharArray()); // 密码组成:sso密钥+用户名+日期,进行md5加密,举例: Digests.md5(secretKey+username+20150101))
                upt.setParams(upt.toString()); // 单点登录识别参数,see: AuthorizingRealm.assertCredentialsMatch
            } catch (Exception ex){
                if (!ex.getMessage().startsWith("msg:")){
                    ex = new AuthenticationException("msg:授权令牌错误,请联系管理员。");
                }
                model.addAttribute("exception", ex);
            }
            try {
                SecurityUtils.getSubject().login(upt);
                return "redirect:" + Encodes.urlDecode2(url);
            } catch (AuthenticationException ae) {
                if (!ae.getMessage().startsWith("msg:")){
                    ae = new AuthenticationException("msg:授权错误,请检查用户配置,若不能解决,请联系管理员。");
                }
                model.addAttribute("exception", ae);
            }
        }
        return "error/403";
    }

    2. 重载org.apache.shiro.realm.AuthorizingRealm类的assertCredentialsMatch方法

    /**
     * 认证密码匹配调用方法
     */
    @Override
    protected void assertCredentialsMatch(AuthenticationToken authcToken,
            AuthenticationInfo info) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        // 若单点登录,则使用单点登录授权方法。
        if (token.toString().equals(token.getParams())){
            // sso密钥+用户名+日期,进行md5加密,举例: Digests.md5(secretKey+username+20150101))
            String secretKey = Global.getConfig("shiro.sso.secretKey");
            String password = Digests.md5(secretKey + token.getUsername() + DateUtils.getDate("yyyyMMdd"));
            if (password.equals(String.valueOf(token.getPassword()))){
                return;
            }
        }
        super.assertCredentialsMatch(token, info);
    }

    3. 实现Shiro无状态访问,如通过传递sessionid参数即可实现会话访问

    这里需要自定义Shiro的SessionManager类,方法是继承org.apache.shiro.web.session.mgt.DefaultWebSessionManager类,重载getSessionId方法,如下:

    public class SessionManager extends DefaultWebSessionManager {
    
        public SessionManager() {
            super();
        }
        
        @Override
        protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
            // 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true
            // 其实这里还可以使用如下参数:cookie中的session名称:如:JSESSIONID=xxx,路径中的 ;JESSIONID=xxx,但建议还是使用 __sid参数。
            String sid = request.getParameter("__sid");
            if (StringUtils.isNotBlank(sid)) {
                // 是否将sid保存到cookie,浏览器模式下使用此参数。
                if (WebUtils.isTrue(request, "__cookie")){
                    HttpServletRequest rq = (HttpServletRequest)request;
                    HttpServletResponse rs = (HttpServletResponse)response;
                    Cookie template = getSessionIdCookie();
                    Cookie cookie = new SimpleCookie(template);
                    cookie.setValue(sid); cookie.saveTo(rq, rs);
                }
                // 设置当前session状态
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                        ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
                return sid;
            }else{
                return super.getSessionId(request, response);
            }
        }
     
    }
  • 相关阅读:
    SAP系统和微信集成的系列教程之十:如何在SAP C4C系统里直接回复消息给微信用户
    SAP系统和微信集成的系列教程之九:如何将微信用户发送给微信公众号的内容自动转存到SAP C4C系统
    SAP系统和微信集成的系列教程之八:100行代码在微信公众号里集成地图搜索功能
    漫谈SAP产品里页面上的Checkbox设计与实现系列之一
    一个SAP成都研究院开发工程师的2020年度总结:未知生,焉知死
    Angular form控件原生HTML代码里ng-reflect-form属性和其值的生成时机
    一个SAP成都研究院开发工程师2020年所有文章列表
    SAP系统和微信集成的系列教程之七:使用Redis存储微信用户和公众号的对话记录
    用shell脚本从git上拉取,项目目录下所有各个子项目代码
    shell脚本小计
  • 原文地址:https://www.cnblogs.com/shenyixin/p/11757309.html
Copyright © 2011-2022 走看看