zoukankan      html  css  js  c++  java
  • shiro token 分析

    1.ShiroConfig.java 定义匿名用户可以访问的资源

      filterMap.put("/webjars/**", "anon");
            filterMap.put("/druid/**", "anon");
            filterMap.put("/api/**", "anon");
            filterMap.put("/sys/login", "anon");
            filterMap.put("/**/*.css", "anon");
            filterMap.put("/**/*.js", "anon");
            filterMap.put("/**/*.html", "anon");
            filterMap.put("/fonts/**", "anon");
            filterMap.put("/plugins/**", "anon");
            filterMap.put("/swagger/**", "anon");
            filterMap.put("/favicon.ico", "anon");
            filterMap.put("/", "anon");
            filterMap.put("/**", "oauth2");           --除了anon,拦截其他所有请求

    2.OAuth2Filter.java 基于shiro的全局过滤器

    继承AuthenticatingFilter 实现createToken、isAccessAllowed、onAccessDenied、onLoginFailure等抽象方法

    import com.google.gson.Gson;
    import io.renren.common.utils.R;
    import org.apache.commons.lang.StringUtils;
    import org.apache.http.HttpStatus;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class OAuth2Filter extends AuthenticatingFilter {

        @Override
        protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token
            String token = getRequestToken((HttpServletRequest) request);

            if(StringUtils.isBlank(token)){
                return null;
            }
            return new OAuth2Token(token);
        }

        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            return false;
        }

        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token,如果token不存在,直接返回401
            String token = getRequestToken((HttpServletRequest) request);
            if(StringUtils.isBlank(token)){
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));
                httpResponse.getWriter().print(json);
                return false;
            }
            System.out.println("onAccessDenied-----------------------onAccessDenied");
            return executeLogin(request, response);
        }

        @Override
        protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            try {
                //处理登录失败的异常
                Throwable throwable = e.getCause() == null ? e : e.getCause();
                R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

                String json = new Gson().toJson(r);
                httpResponse.getWriter().print(json);
            } catch (IOException e1) {

            }
            return false;
        }

        /**
         * 获取请求的token
         */
        private String getRequestToken(HttpServletRequest httpRequest){
            //从header中获取token
            String token = httpRequest.getHeader("token");

            //如果header中不存在token,则从参数中获取token
            if(StringUtils.isBlank(token)){
                token = httpRequest.getParameter("token");
            }
            return token;
        }
    }

    如果成功获得token 则继续调用父类中executeLogin方法,此方法实现如下

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
            AuthenticationToken token = createToken(request, response);
            if (token == null) {
                String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                        "must be created in order to execute a login attempt.";
                throw new IllegalStateException(msg);
            }
            try {
           // 创建主题,然后继续调用Realm中的登入认证方法doGetAuthenticationInfo Subject subject
    = getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } }

    调用子类中的createToken方法获得token对象,将token对象赋值给shiro subject 对象,从而在后面的认证方法中获得token

    3.将OAuth2Realm 注册到Shiro Seurity中,ShiroConfig.securityManager

     1 package io.renren.config;
     2 
     3 import io.renren.modules.sys.oauth2.OAuth2Filter;
     4 import io.renren.modules.sys.oauth2.OAuth2Realm;
     5 import org.apache.shiro.mgt.SecurityManager;
     6 import org.apache.shiro.session.mgt.SessionManager;
     7 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
     8 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
     9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    10 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    11 import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    12 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    13 import org.springframework.context.annotation.Bean;
    14 import org.springframework.context.annotation.Configuration;
    15 
    16 import javax.servlet.Filter;
    17 import java.util.HashMap;
    18 import java.util.LinkedHashMap;
    19 import java.util.Map;
    20 
    21 
    22 @Configuration
    23 public class ShiroConfig {
    24 
    25     @Bean("sessionManager")
    26     public SessionManager sessionManager(){
    27         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    28         sessionManager.setSessionValidationSchedulerEnabled(true);
    29         sessionManager.setSessionIdCookieEnabled(false);
    30         System.out.println("获得sessionManager:" + sessionManager);
    31         return sessionManager;
    32     }
    33 
    34     @Bean("securityManager")
    35     public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {
    36         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    37         securityManager.setRealm(oAuth2Realm);
    38         securityManager.setSessionManager(sessionManager);
    39         System.out.println("获得SecurityManager:" + securityManager);
    40         return securityManager;
    41     }
    42 
    43     @Bean("shiroFilter")
    44     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    45         ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
    46         shiroFilter.setSecurityManager(securityManager);
    47 
    48         //oauth过滤
    49         Map<String, Filter> filters = new HashMap<>();
    50         filters.put("oauth2", new OAuth2Filter());
    51         shiroFilter.setFilters(filters);
    52 
    53         Map<String, String> filterMap = new LinkedHashMap<>();
    54         filterMap.put("/webjars/**", "anon");
    55         filterMap.put("/druid/**", "anon");
    56         filterMap.put("/api/**", "anon");
    57         filterMap.put("/sys/login", "anon");
    58         filterMap.put("/**/*.css", "anon");
    59         filterMap.put("/**/*.js", "anon");
    60         filterMap.put("/**/*.html", "anon");
    61         filterMap.put("/fonts/**", "anon");
    62         filterMap.put("/plugins/**", "anon");
    63         filterMap.put("/swagger/**", "anon");
    64         filterMap.put("/favicon.ico", "anon");
    65         filterMap.put("/", "anon");
    66         filterMap.put("/**", "oauth2");
    67         shiroFilter.setFilterChainDefinitionMap(filterMap);
    68         System.out.println("获得shiroFilter:" + shiroFilter);
    69         return shiroFilter;
    70     }
    71 
    72     @Bean("lifecycleBeanPostProcessor")
    73     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    74         return new LifecycleBeanPostProcessor();
    75     }
    76 
    77     @Bean
    78     public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    79         DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
    80         proxyCreator.setProxyTargetClass(true);
    81         return proxyCreator;
    82     }
    83 
    84     @Bean
    85     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    86         AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    87         advisor.setSecurityManager(securityManager);
    88         return advisor;
    89     }
    90 
    91 }
    View Code

    4.每次请求都会先调用OAuth2Realm中的doGetAuthenticationInfo方法验证token的合法性,然后再调用doGetAuthorizationInfo验证权限

    5.通过common.js判断当前客户端是否缓存了token,如果没有则跳转至login.html

    //登录token
    var token = localStorage.getItem("token");
    if(token == 'null'){
        parent.location.href = baseURL + 'login.html';
    }

    6.登入页面输入用户名、密码之后 缓存token,并跳转至index.html

    login: function () {
                var data = "username="+vm.username+"&password="+vm.password;
                $.ajax({
                    type: "POST",
                    url: baseURL + "sys/login",
                    data: data,
                    dataType: "json",
                    success: function(r){
                        if(r.code == 0){//登录成功
                            localStorage.setItem("token", r.token);
                            parent.location.href ='index.html';
                        }else{
                            vm.error = true;
                            vm.errorMsg = r.msg;
                        }
                    }
                });
            }
    View Code

    7.LoginController.java

    /**
         * 登录
         */
        @RequestMapping(value = "/sys/login", method = RequestMethod.POST)
        public Map<String, Object> login(String username, String password)throws IOException {
            //用户信息
            SysUserEntity user = sysUserService.queryByUserName(username);
    
            //账号不存在、密码错误
            if(user == null || !user.getPassword().equals(new Sha256Hash(password, user.getSalt()).toHex())) {
                return R.error("账号或密码不正确");
            }
    
            //账号锁定
            if(user.getStatus() == 0){
                return R.error("账号已被锁定,请联系管理员");
            }
    
            //生成token,并保存到数据库
            R r = sysUserTokenService.createToken(user.getUserId());
            return r;
        }

    8. 数据库token表结构,该表结构改成redis即可实现sso单点登入功能

      Field Type Comment
    user_id bigint(20) NOT NULL  
      token varchar(100) NOT NULL token
      expire_time datetime NULL 过期时间
      update_time datetime NULL 更新时间
  • 相关阅读:
    导包路径
    django导入环境变量 Please specify Django project root directory
    替换django的user模型,mysql迁移表报错 django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependen cy user.0001_initial on database 'default'.
    解决Chrome调试(debugger)
    check the manual that corresponds to your MySQL server version for the right syntax to use near 'order) values ('徐小波','XuXiaoB','男','1',' at line 1")
    MySQL命令(其三)
    MySQL操作命令(其二)
    MySQL命令(其一)
    [POJ2559]Largest Rectangle in a Histogram (栈)
    [HDU4864]Task (贪心)
  • 原文地址:https://www.cnblogs.com/rigid/p/7514708.html
Copyright © 2011-2022 走看看