• litemall源码阅读4.04 litemall-admin-api的权限控制与安全配置


    最近在这里卡了好久了。litemall的权限控制使用的是shrio。主要阅读了这篇文章这篇文章

    在这里至少有四个文件与shrio配置相关:

    src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java

    src/main/java/org/linlinjava/litemall/admin/config/ShiroExceptionHandler.java

    src/main/java/org/linlinjava/litemall/admin/shiro/AdminAuthorizingRealm.java

    src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java

    Shiro是主要配置类,@Configuration在某种意义上就是将这个类转化为一个spring的bean配置类。

    在这个类里主要定义了Shiro工作需要的bean。

    @Configuration
    public class ShiroConfig {

    @Bean
    public Realm realm() {
    return new AdminAuthorizingRealm();  //返回认证和授权的的自定义realm
    }

       //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    filterChainDefinitionMap.put("/admin/auth/login", "anon");//不会拦截匿名用户访问登录页
    filterChainDefinitionMap.put("/admin/auth/401", "anon");//不会拦截匿名用户访问401
    filterChainDefinitionMap.put("/admin/auth/index", "anon");//不会拦截匿名用户访问index
    filterChainDefinitionMap.put("/admin/auth/403", "anon");//不会拦截匿名用户访问403
    filterChainDefinitionMap.put("/admin/index/*", "anon");//不会拦截匿名用户访问/admin/index下的任意内容

    filterChainDefinitionMap.put("/admin/**", "authc");//不会拦截已经登录的用户访问/admin下的内容
    shiroFilterFactoryBean.setLoginUrl("/admin/auth/401");//登录成功后的url跳转
    shiroFilterFactoryBean.setSuccessUrl("/admin/auth/index");//登录成功后的url跳转
    shiroFilterFactoryBean.setUnauthorizedUrl("/admin/auth/403");//登录失败后的url跳转
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);//设置拦截链
    return shiroFilterFactoryBean;
    }
      //返回自定义的sessionmanager。也就是AdminWebSessionManager.java中定义的对象
    @Bean
    public SessionManager sessionManager() {

    return new AdminWebSessionManager();
    }
      //返回securitymanager,配置了realm与sessionmanager。
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(realm());
    securityManager.setSessionManager(sessionManager());
    return securityManager;
    }
      //开启shrio注解的配置
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
    new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
    }
      //开启shrio注解的配置
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
    creator.setProxyTargetClass(true);
    return creator;
    }
    }

    接下来是自定义realm了。realm主要有两个功能,1,认证,2授权。

    类内部有3个bean。

    @Autowired
    private LitemallAdminService adminService;  //管理员信息表。用于保存管理员的信息,比如管理员的角色,密码,头像等。
    @Autowired
    private LitemallRoleService roleService;  //角色信息表,用于将管理员分为多种角色。
    @Autowired
    private LitemallPermissionService permissionService;  //角色权限表,用于保存每种角色分别有什么权限。

    先看认证返回函数。这个函数会在后台的src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java文件调用

    currentUser.login(new UsernamePasswordToken(username, password));

    时调用。

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
      //upToken中保存了登录的用户名和密码
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    String username = upToken.getUsername();
    String password = new String(upToken.getPassword());
      //异常处理,在src/main/java/org/linlinjava/litemall/admin/config/ShiroExceptionHandler.java中,下面会讲
    if (StringUtils.isEmpty(username)) {
    throw new AccountException("用户名不能为空");
    }
    if (StringUtils.isEmpty(password)) {
    throw new AccountException("密码不能为空");
    }
      //使用adminservice获取当前username管理员账户信息。
    List<LitemallAdmin> adminList = adminService.findAdmin(username);
    Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
    if (adminList.size() == 0) {
    throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
    }
    LitemallAdmin admin = adminList.get(0);
      //使用加盐加密算法查看用户名与密码是否与数据库中的数据匹配
    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    if (!encoder.matches(password, admin.getPassword())) {
    throw new UnknownAccountException("找不到用户(" + username + ")的帐号信息");
    }
      //如果一切成功则返回一个新的认证信息。
    return new SimpleAuthenticationInfo(admin, password, getName());
    }

    接下来看授权,授权信息会在前端点击url时进行调用。以确定是否有权限进行操作。

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    if (principals == null) {
    throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
    }
      //获取当前操作的用户信息
    LitemallAdmin admin = (LitemallAdmin) getAvailablePrincipal(principals);
      //获取角色信息。
    Integer[] roleIds = admin.getRoleIds();
      //根据角色信息表和角色权限表,返回当前用户的操作权限。
    Set<String> roles = roleService.queryByIds(roleIds);
    Set<String> permissions = permissionService.queryByRoleIds(roleIds);
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    info.setRoles(roles);
    info.setStringPermissions(permissions);
    return info;
    }

    之后我们看src/main/java/org/linlinjava/litemall/admin/shiro/AdminWebSessionManager.java

    首次登陆的时候,函数getSessionId会在进行上面realm验证前进行调用。因为是第一次登陆,所以这时候sessionId为空值。

    如果登陆成功,则src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java会返回登陆后的sessionId值,

     当第二次登陆,就可以获取到相应的LOGIN_TOKEN_KEY了。再将该值返回,用于服务器匹配当前session的状态。

    public static final String LOGIN_TOKEN_KEY = "X-Litemall-Admin-Token";
    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
    String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
    if (!StringUtils.isEmpty(id)) {
    request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
    request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
    request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
    return id;
    } else {
    return super.getSessionId(request, response);
    }
    }

    最后是src/main/java/org/linlinjava/litemall/admin/config/ShiroExceptionHandler.java

    这个文件的知识点属于springMVC的内容,就是设置一个全局的异常拦截器。但这个文件里只拦截了Shiro抛出的异常。

    比如,当用户的session过期或者用户无相应的页面权限,抛出的异常就会被该文件拦截到。

    //处理登录异常
    @ExceptionHandler(AuthenticationException.class)
    @ResponseBody
    public Object unauthenticatedHandler(AuthenticationException e) {
    logger.warn(e.getMessage(), e);
    return ResponseUtil.unlogin();
    }
    //处理权限异常
    @ExceptionHandler(AuthorizationException.class)
    @ResponseBody
    public Object unauthorizedHandler(AuthorizationException e) {
    logger.warn(e.getMessage(), e);
    return ResponseUtil.unauthz();
    }
    
    
    
  • 相关阅读:
    美达飞凡16X DVD起死回生记
    vs2k5 中asp.net "Web Site Administration Tool "使用中遇到的问题
    有关sqlserver的锁
    基于dotnet2.0的联通sgip1.2协议二级网关源码
    .net winform下TreeNode在没有子结点时也显示+号的解决办法
    小胜凭智, 大胜靠德
    寄语八十年代的新一代
    PHP+APACHE+MYSQL+WINDOWS 环境配置秘笈,一定行!!!!
    JS获取当前屏幕分辨率
    godaddy免费空间安装wordpress教程之500错误的解决办法/读写权限修改
  • 原文地址:https://www.cnblogs.com/xiaoBay/p/14123898.html
Copyright © 2020-2023  润新知