Spring MVC интегрирует контроль разрешений Shiro

Spring MVC Безопасность Shiro

Apache Shiro — это мощная и гибкая платформа безопасности с открытым исходным кодом, которая обеспечивает точную аутентификацию, авторизацию, управление сеансами, а также шифрование и другие распространенные процессы управления безопасностью в приложениях уровня предприятия. Основная цель Apache Shiro — сделать его простым в использовании и понятным. Иногда процесс управления безопасностью может быть очень сложным, что может стать головной болью для разработчиков, но это не обязательно. Фреймворки должны максимально маскировать сложность и предоставлять чистый и интуитивно понятный API, который упрощает работу разработчиков и обеспечивает безопасность их приложений. На этот раз мы поговорим о том, как использовать Shiro для реализации контроля разрешений в веб-приложениях Spring.

Функции

Apache Shiro — это комплексная платформа безопасности приложений с множеством функций. На следующем рисунке показаны наиболее важные функции в Широ:

Основная цель Широ — «четыре краеугольных камня безопасности приложений» — аутентификация, авторизация, управление сессиями и шифрование:

  • Аутентификация: также известная как «логин», чтобы подтвердить поведение владельца пользователя.
  • Авторизация: процесс управления доступом, то есть определение того, какие пользователи могут получить доступ к какому контенту.
  • Управление сеансами. Возможность управления сеансами для конкретных пользователей, даже в не веб-приложениях, является одной из сильных сторон Широ.
  • Технология шифрования. Используйте алгоритмы шифрования для обеспечения безопасности данных и простоты их использования.

Архитектура

Из общего концептуального понимания архитектура Широ имеет три основных понятия: Subject (субъект, то есть пользователь), Security Manager (менеджер безопасности) и Realms (домен). Следующая диаграмма описывает взаимосвязь между этими компонентами:

Эти основные компоненты можно понимать следующим образом:

  • Субъект (subject): Субъект представляет собой совокупность данных, специфичных для пользователя, работающего в данный момент. Субъектом может быть человек или сторонняя служба, демон, задание cron или что-то в этом роде, то есть почти все, что взаимодействует с приложением.
  • Менеджер безопасности: является ядром архитектуры Широ и играет роль, аналогичную «зонту», в основном отвечает за координацию различных внутренних компонентов для формирования сети безопасности.
  • Realms: «мост» между Широ и данными безопасности приложений. Когда необходимо фактически взаимодействовать с данными, связанными с безопасностью, такими как учетные записи пользователей, для выполнения аутентификации и авторизации, Широ будет получать эти данные из Realms.

подготовка данных

В веб-приложениях контроль безопасности в основном включает в себя понятия ролей, ресурсов и разрешений (какие роли могут получить доступ к каким ресурсам).У пользователя может быть несколько ролей, и роль также может иметь доступ к нескольким ресурсам, то есть роли могут соответствовать для нескольких разрешений. Для реализации дизайна базы данных нам необходимо построить как минимум 5 таблиц: таблица пользователей, таблица ролей, таблица ресурсов, таблица ролей-ресурсов, таблица ролей пользователей, структура этих 5 таблиц следующая: пользовательская таблица:

id username password
1 Чжан Сан 123456
2 Ли Си 666666
3 Ван Ву 000000

Таблица ролей:

id rolename
1 администратор
2 управляющий делами
3 штат сотрудников

Лист ресурсов:

id resname
1 /user/add
2 /user/delete
3 /compony/info

Таблица ролей-ресурсов:

id roleid resid
1 1 1
2 1 2
3 2 3

Таблица ролей пользователей:

id userid roleid
1 1 1
2 1 2
3 1 3

Соответствующие классы POJO следующие:

/**
 * 用户
 */
public class User {
 private Integer id;
 private String username;
 private String password;
    //getter & setter...
}
/**
 * 角色
 */
public class Role {
    private String id;
    private String rolename;
}
/**
 * 资源
 */
public class Resource {
    private String id;
    private String resname;
}
/**
 * 角色-资源
 */
public class RoleRes {
    private String id;
    private String roleid;
    private String resid;
}
/**
 * 用户-角色
 */
public class UserRole {
    private String id;
    private String userid;
    private String roleid;
}

Подробные инструкции по интеграции Spring с Shiro см. в моем блоге.«Интеграция Apache Shiro в приложения Spring». Вот дополнение: зависимости Широ нужно вводить заранее, открыватьmvnrepository.com, ищем Shiro, нам нужны первые три зависимости, а именно Shiro-Core, Shiro-Web и Shiro-Spring, в качестве примера возьмем проект Maven, вpom.xmlсередина<dependencies>Добавьте следующие зависимости под узлом:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

существуетapplication-context.xmlнужно настроить такshiroFilter bean:

<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <!-- 登录页面 -->
    <property name="loginUrl" value="/login"/>
    <!-- 登录成功后的页面 -->
    <property name="successUrl" value="/index"/>
    <!-- 非法访问跳转的页面 -->
    <property name="unauthorizedUrl" value="/403"/>
    <!-- 权限配置 -->
    <property name="filterChainDefinitions">
        <value>
            <!-- 无需认证即可访问的静态资源,还可以添加其他 url -->
            /static/** = anon
            <!-- 除了上述忽略的资源,其他所有资源都需要认证后才能访问 -->
            /** = authc
        </value>
    </property>
</bean>

Далее вам нужно определить Realm, Пользовательский Realm интегрируется самAuthorizingRealmсвоего рода:

public class MyRealm extends AuthorizingRealm {
 @Autowired
 private UserService userService;
 /**
  * 验证权限
  */
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  String loginName = SecurityUtils.getSubject().getPrincipal().toString();
  if (loginName != null) {
   String userId = SecurityUtils.getSubject().getSession().getAttribute("userSessionId").toString();
   // 权限信息对象,用来存放查出的用户的所有的角色及权限
   SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
   // 用户的角色集合
   ShiroUser shiroUser = (ShiroUser) principalCollection.getPrimaryPrincipal();
         info.setRoles(shiroUser.getRoles());
         info.addStringPermissions(shiroUser.getUrlSet());
   return info;
  }
  return null;
 }
 /**
  * 认证回调函数,登录时调用
  */
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
  String username = (String) token.getPrincipal();
  User user = new User();
        sysuser.setUsername(username);
  try {
   List<SysUser> users = userService.findByNames(user);
            List<String> roleList= userService.selectRoleNameListByUserId(users.get(0).getId());
   if (users.size() != 0) {
    String pwd = users.get(0).getPassword();
    // 当验证都通过后,把用户信息放在 session 里
    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute("userSession", users.get(0));
    session.setAttribute("userSessionId", users.get(0).getId());
    session.setAttribute("userRoles", org.apache.commons.lang.StringUtils.join(roleList,","));
                return new SimpleAuthenticationInfo(username,users.get(0).getPassword());
   } else {
                // 没找到该用户
    throw new UnknownAccountException();
   }
  } catch (Exception e) {
   System.out.println(e.getMessage());
  }
  return null;
 }
 /**
     * 更新用户授权信息缓存.
     */
 public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
  super.clearCachedAuthorizationInfo(principals);
 }
 /**
     * 更新用户信息缓存.
     */
 public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
  super.clearCachedAuthenticationInfo(principals);
 }
 /**
  * 清除用户授权信息缓存.
  */
 public void clearAllCachedAuthorizationInfo() {
  getAuthorizationCache().clear();
 }
 /**
  * 清除用户信息缓存.
  */
 public void clearAllCachedAuthenticationInfo() {
  getAuthenticationCache().clear();
 }
 /**
  * 清空所有缓存
  */
 public void clearCache(PrincipalCollection principals) {
  super.clearCache(principals);
 }
 /**
  * 清空所有认证缓存
  */
 public void clearAllCache() {
  clearAllCachedAuthenticationInfo();
  clearAllCachedAuthorizationInfo();
 }
}

Наконец, определите контроллер входа пользователя, чтобы принимать запрос пользователя на вход:

@Controller
public class UserController {
    /**
     * 用户登录
     */
    @PostMapping("/login")
    public String login(@Valid User user,BindingResult bindingResult,RedirectAttributes redirectAttributes){
        try {
            if(bindingResult.hasErrors()){
                return "login";
            }
            //使用权限工具进行认证,登录成功后跳到 shiroFilter bean 中定义的 successUrl
            SecurityUtils.getSubject().login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));
            return "redirect:index";
        } catch (AuthenticationException e) {
            redirectAttributes.addFlashAttribute("message","用户名或密码错误");
            return "redirect:login";
        }
    }
    /**
     * 注销登录
     */
    @GetMapping("/logout")
    public String logout(RedirectAttributes redirectAttributes ){
        SecurityUtils.getSubject().logout();
        return "redirect:login";
    }
}