Создайте свой собственный фоновый фреймворк SpringBoot с нуля (14)

Spring Boot Java Shiro
Всем привет, в этой главе мы добавляем функцию интерфейса защиты разрешений shiro. Если у вас есть какие-либо вопросы, пожалуйста, свяжитесь со мной по адресу mr_beany@163.com. Также попросите руководства великих богов, спасибо

Один: что такое сиро

Shiro — это платформа разрешений с открытым исходным кодом для платформы Java для аутентификации и авторизации доступа. В частности, обеспечивается поддержка следующих элементов:

  • Пользователи, роли, разрешения (только разрешения на операции, разрешения на данные должны быть тесно связаны с потребностями бизнеса), ресурсы (url).
  • Пользователям назначаются роли, а роли определяют разрешения.
  • Авторизация доступа поддерживает роли или разрешения, а также поддерживает многоуровневые определения разрешений.

Короче говоря, shiro использует свою собственную конфигурацию, чтобы обеспечить доступ к интерфейсу только с указанными ролями или разрешениями, что обеспечивает безопасность интерфейса.

Два: добавить зависимости shiro

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring-boot-web-starter</artifactId>
   <version>1.4.0-RC2</version>
</dependency>

Третье: создайте таблицы базы данных, такие как разрешения, роли, таблицы отношений разрешений ролей пользователей и т. д.

1: изменить оригиналuserInfoповерхность

Добавить кpassword(用户密码,经过加密之后的),salt(加密盐值)

После модификации структура выглядит следующим образом


вpasswordДанные генерируются после шифрования 123456

2: Добавить таблицу ролей

CREATE TABLE `sys_role` (
  `id` varchar(36) NOT NULL COMMENT '角色名称',
  `role_name` varchar(255) DEFAULT NULL COMMENT '角色名称,用于显示',
  `role_desc` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `role_value` varchar(255) DEFAULT NULL COMMENT '角色值,用于权限判断',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `is_disable` int(1) DEFAULT NULL COMMENT '是否禁用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表';

3: Добавить таблицу взаимосвязей ролей пользователей

CREATE TABLE `user_role` (
  `id` varchar(36) NOT NULL,
  `user_id` varchar(36) DEFAULT NULL COMMENT '用户ID',
  `role_id` varchar(36) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色关系表';

4: Добавить таблицу разрешений

CREATE TABLE `sys_perm` (
  `id` varchar(36) NOT NULL,
  `perm_name` varchar(255) DEFAULT NULL COMMENT '权限名称',
  `perm_desc` varchar(255) DEFAULT NULL COMMENT '权限描述',
  `perm_value` varchar(255) DEFAULT NULL COMMENT '权限值',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `is_disable` int(1) DEFAULT NULL COMMENT '是否禁用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5: Добавить таблицу разрешений роли

CREATE TABLE `role_perm` (
  `id` varchar(36) NOT NULL,
  `perm_id` varchar(32) DEFAULT NULL COMMENT '权限id',
  `role_id` varchar(32) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限表';

Отношения между таблицами примерно следующие: один пользователь соответствует нескольким ролям, а одна роль использует несколько разрешений.

Простите мой острый куриный рисунок


Четвертое: используйте генератор кода для создания картографа, дао, сервиса и контроллера для четырех новых таблиц.

Вы можете сгенерировать его напрямую. Из-за слишком большого количества кода он не будет отображаться здесь. Для получения подробной информации вы можете перейти кОблако кодассылка выше

Пятое: метод добавления ролей запросов и разрешений

UserRoleMapper.xml

<!--根据用户id查询该用户所有角色-->
<select id="getRolesByUserId" resultType="string" parameterType="string">
  select sr.role_value
  from user_role ur
  left join sys_role sr on ur.role_id = sr.id
  where ur.user_id = #{userId,jdbcType=VARCHAR}
  and sr.is_disable = 0
</select>

UserRoleMapper.java

List<String> getRolesByUserId(String userId);

RolePermMapper.xml

<select id="getPermsByUserId" resultType="string" parameterType="string">
  select distinct
      p.perm_value
  from
      sys_perm p,
      role_perm rp,
      user_role ur
  where
      p.id = rp.perm_id
      and ur.role_id = rp.role_id
      and ur.user_id = #{userId,jdbcType=VARCHAR}
      and p.is_disable = 0
</select>

Пояснение, здесь запрашиваются все разрешения, соответствующие пользователю.Поскольку роли и разрешения находятся в отношениях «многие ко многим», разрешения запрашиваемого пользователя могут дублироваться.distinctИди потяжелее.

RolePermMapper.java

List<String> getPermsByUserId(String userId);

Измените класс сущности userInfo следующим образом.

package com.example.demo.model;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Transient;
import java.util.HashSet;
import java.util.Set;

/**
 * @author 张瑶
 * @Description:
 * @time 2018/4/18 11:55
 */
public class UserInfo {

    /**
     * 主键
     */
    @Id
    private String id;

    /**
     * 用户名
     */
    @Column(name = "user_name")
    private String userName;

    private String password;

    /**
     * 加密盐值
     */
    private String salt;

    /**
     * 用户所有角色值,用于shiro做角色权限的判断
     */
    @Transient
    private Set<String> roles;

    /**
     * 用户所有权限值,用于shiro做资源权限的判断
     */
    @Transient
    private Set<String> perms;



    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set<String> getRoles() {
        return roles;
    }

    public void setRoles(Set<String> roles) {
        this.roles = roles;
    }

    public Set<String> getPerms() {
        return perms;
    }

    public void setPerms(Set<String> perms) {
        this.perms = perms;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }
}

Шесть: Пользовательский мир

Создать ядро→широ→CustomRealm.java

1: Реализация аутентификации при входе

В Широ информация о пользователе, роли и разрешениях в приложении окончательно получается через Realm. Обычно в Realm информация для аутентификации, которая нужна Широ, получается непосредственно из нашего источника данных. Можно сказать, что Realm — это DAO, посвященная системам безопасности.
Процесс аутентификации Широ в конечном итоге будет выполняться Realm, после чего RealmgetAuthenticationInfo(token)метод.

Этот метод в основном выполняет следующие операции:

  • Проверьте отправленную информацию о токене для аутентификации
  • Получить информацию о пользователе из источника данных (обычно из базы данных) на основе информации о токене.
  • Сопоставьте и проверьте информацию о пользователе.
  • Если проверка пройдена, она вернет инкапсулированную информацию о пользователе.AuthenticationInfoпример.
  • Брошен, если проверка не удаласьAuthenticationExceptionИнформация об исключении.

Что нам нужно сделать в нашем приложении, так это настроить класс Realm, наследовать абстрактный класс AuthorizingRealm, перегрузить doGetAuthenticationInfo() и переопределить метод для получения информации о пользователе.

2: Реализация полномочий по ссылкам

Переопределите doGetAuthorizationInfo(), чтобы определить логику получения ролей и разрешений пользователя, а также оценить разрешения для shiro.

Полный код выглядит следующим образом

package com.example.demo.core.shiro;


import com.example.demo.model.UserInfo;
import com.example.demo.service.RolePermService;
import com.example.demo.service.UserInfoService;
import com.example.demo.service.UserRoleService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
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;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义如何查询用户信息,如何查询用户的角色和权限,如何校验密码等逻辑
 */
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private UserInfoService userService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private RolePermService rolePermService;

    /**
     * 告诉shiro如何根据获取到的用户信息中的密码和盐值来校验密码
     */
    {
        //设置用于匹配密码的CredentialsMatcher
        HashedCredentialsMatcher hashMatcher = new HashedCredentialsMatcher();
        hashMatcher.setHashAlgorithmName("md5");
        hashMatcher.setStoredCredentialsHexEncoded(true);
        //加密的次数
        hashMatcher.setHashIterations(1024);
        this.setCredentialsMatcher(hashMatcher);
    }


    /**
     *  定义如何获取用户的角色和权限的逻辑,给shiro做权限判断
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        UserInfo user = (UserInfo) getAvailablePrincipal(principals);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(user.getRoles());
        info.setStringPermissions(user.getPerms());
        return info;
    }

    /**
     * 定义如何获取用户信息的业务逻辑,给shiro做登录
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        if (username == null) {
            throw new AccountException("Null usernames are not allowed by this realm.");
        }
        UserInfo userDB = userService.selectBy("userName",username);
        if (userDB == null) {
            throw new UnknownAccountException("No account found for admin [" + username + "]");
        }
        //查询用户的角色和权限存到SimpleAuthenticationInfo中,这样在其它地方
        //SecurityUtils.getSubject().getPrincipal()就能拿出用户的所有信息,包括角色和权限
        List<String> roleList = userRoleService.getRolesByUserId(userDB.getId());
        List<String> permList = rolePermService.getPermsByUserId(userDB.getId());
        Set<String> roles = new HashSet(roleList);
        Set<String> perms = new HashSet(permList);
        userDB.setRoles(roles);
        userDB.setPerms(perms);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDB, userDB.getPassword(), getName());
        info.setCredentialsSalt(ByteSource.Util.bytes(userDB.getSalt()));
        return info;

    }

}

Семь: добавить конфигурацию широ

Создать ядро→конфигуратор→Широконфигурер

package com.example.demo.core.configurer;

import com.example.demo.core.shiro.CustomRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

@Configuration
public class ShiroConfigurer {

    /**
     * 注入自定义的realm,告诉shiro如何获取用户信息来做登录或权限控制
     */
    @Bean
    public Realm realm() {
        return new CustomRealm();
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        creator.setUsePrefix(true);
        return creator;
    }

    /**
     * 这里统一做鉴权,即判断哪些请求路径需要用户登录,哪些请求路径不需要用户登录
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        chain.addPathDefinition( "/userInfo/selectById", "authc, roles[admin]");
        chain.addPathDefinition( "/logout", "anon");
        chain.addPathDefinition( "/userInfo/selectAll", "anon");
        chain.addPathDefinition( "/userInfo/login", "anon");
        chain.addPathDefinition( "/**", "authc");
        return chain;
    }
}

Широ предоставляет и ряд фильтров по умолчанию, мы можем использовать эти фильтры для настройки разрешений для управления указанным URL-адресом:

аббревиатура конфигурации соответствующий фильтр Функции
anon AnonymousFilter Указанный URL-адрес может быть доступен анонимно
authc FormAuthenticationFilter Указанный URL-адрес требует входа в форму, которая будет получена из запроса по умолчанию.username,password,rememberMeДождитесь параметров и попробуйте войти. Если вы не можете войти, он перейдет на путь, настроенный loginUrl. Мы также можем использовать этот фильтр в качестве логики входа по умолчанию, но обычно мы сами пишем логику входа в контроллер, а информацию, возвращаемую ошибкой, можно настроить, если мы напишем ее сами.
authcBasic BasicHttpAuthenticationFilter Для указания URL-адреса требуется базовый вход в систему
logout LogoutFilter Фильтр выхода, настройте указанный URL-адрес для реализации функции выхода из системы, что очень удобно.
noSessionCreation NoSessionCreationFilter Отключить создание сеанса
perms PermissionsAuthorizationFilter Требуется указанное разрешение на доступ
port PortFilter Необходимо указать порт для доступа
rest HttpMethodPermissionFilter Преобразуйте метод http-запроса в соответствующий глагол, чтобы создать строку разрешения.
roles RolesAuthorizationFilter Требуется указанная роль для доступа
ssl SslFilter Требуется HTTPS-запрос для доступа
user UserFilter Для доступа требуется авторизованный пользователь или пользователь с функцией «запомнить меня».

Восемь: добавьте метод входа в UserInfoController

@PostMapping("/login")
public RetResult<UserInfo> login(String userName, String password) {
    Subject currentUser = SecurityUtils.getSubject();
    //登录
    try {
        currentUser.login(new UsernamePasswordToken(userName, password));
    }catch (IncorrectCredentialsException i){
        throw new ServiceException("密码输入错误");
    }
    //从session取出用户信息
    UserInfo user = (UserInfo) currentUser.getPrincipal();
    return RetResponse.makeOKRsp(user);
}

Девятое: добавить данные разрешений в базу данных

INSERT INTO `sys_role` VALUES ('1', '财务', '负责发工资', 'cw', '2018-05-26 00:37:52', null, '0');
INSERT INTO `sys_role` VALUES ('2', '人事', '负责员工', 'rs', '2018-05-26 00:38:18', null, '0');
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '1', '2');
INSERT INTO `sys_perm` VALUES ('1', '创建', '创建权限', 'create', '2018-05-26 00:39:16', null, '0');
INSERT INTO `sys_perm` VALUES ('2', '删除', '删除权限', 'delete', '2018-05-26 00:39:39', null, '0');
INSERT INTO `sys_perm` VALUES ('3', '修改', '修改权限', 'update', '2018-05-26 00:39:58', null, '0');
INSERT INTO `sys_perm` VALUES ('4', '查询', '查询权限', 'select', '2018-05-26 00:40:16', null, '0');
INSERT INTO `role_perm` VALUES ('1', '1', '1');
INSERT INTO `role_perm` VALUES ('2', '2', '1');
INSERT INTO `role_perm` VALUES ('3', '1', '2');
INSERT INTO `role_perm` VALUES ('4', '2', '2');
INSERT INTO `role_perm` VALUES ('5', '3', '2');
INSERT INTO `role_perm` VALUES ('6', '4', '2');

Десять: добавить shiroUtilsController

package com.example.demo.controller;

import com.example.demo.core.ret.ServiceException;
import com.example.demo.model.UserInfo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("shiroUtils")
public class ShiroUtilsController {

    @GetMapping("/noLogin")
    public void noLogin() {
        throw new UnauthenticatedException();
    }

    @GetMapping("/noAuthorize")
    public void noAuthorize() {
        throw new UnauthorizedException();
    }


    @PostMapping("/getNowUser")
    public UserInfo getNowUser() {
        UserInfo u = (UserInfo) SecurityUtils.getSubject().getPrincipal();
        return u;
    }

}

Одиннадцать: Добавить код исключения ошибки

package com.example.demo.core.ret;

/**
 * @Description: 响应码枚举,参考HTTP状态码的语义
 * @author 张瑶
 * @date 2018/4/19 09:42
 */
public enum RetCode {

   // 成功
   SUCCESS(200),

   // 失败
   FAIL(400),

   // 未认证(签名错误)
   UNAUTHORIZED(401),

   /** 未登录 */
   UNAUTHEN(4401),

   /** 未授权,拒绝访问 */
   UNAUTHZ(4403),

   // 服务器内部错误
   INTERNAL_SERVER_ERROR(500);

   public int code;

   RetCode(int code) {
      this.code = code;
   }
}

Двенадцать: добавить перехват исключений

@Override
	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		exceptionResolvers.add(new HandlerExceptionResolver() {
			@Override
			public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
				RetResult<Object> result = new RetResult<Object>();
				// 业务失败的异常,如“账号或密码错误”
				if (e instanceof ServiceException) {
					result.setCode(RetCode.FAIL).setMsg(e.getMessage()).setData(null);
					LOGGER.info(e.getMessage());
				} else if (e instanceof NoHandlerFoundException) {
					result.setCode(RetCode.NOT_FOUND).setMsg("接口 [" + request.getRequestURI() + "] 不存在");
				} else if (e instanceof UnauthorizedException) {
					result.setCode(RetCode.UNAUTHEN).setMsg("用户没有访问权限").setData(null);
				}else if (e instanceof UnauthenticatedException) {
					result.setCode(RetCode.UNAUTHEN).setMsg("用户未登录").setData(null);
				}else if (e instanceof ServletException) {
					result.setCode(RetCode.FAIL).setMsg(e.getMessage());
				} else {
					result.setCode(RetCode.INTERNAL_SERVER_ERROR).setMsg("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
					String message;
					if (handler instanceof HandlerMethod) {
						HandlerMethod handlerMethod = (HandlerMethod) handler;
						message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", request.getRequestURI(), handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod()
								.getName(), e.getMessage());
					} else {
						message = e.getMessage();
					}
					LOGGER.error(message, e);
				}
				responseResult(response, result);
				return new ModelAndView();
			}
		});
	}

Тринадцать: добавить конфигурацию пути широ

существуетapplication.propertiesдобавлено в

#shiro配置
#用户未登录
shiro.loginUrl=/shiroUtils/noLogin
#用户没有权限
shiro.unauthorizedUrl=/shiroUtils/noAuthorize

Четырнадцать: Тест

Введите localhost:8080/userInfo/selectAll

Мы видим, что можем получить данные


Введите localhost:8080/userInfo/selectById


затем войдите в систему


Посетите localhost:8080/userInfo/selectById еще раз, и мы увидим, что shiro вступил в силу.


Изменить права доступа пользователя, перезагрузка, повторно войти

chain.addPathDefinition( "/userInfo/selectById", "authc, roles[cw]");

Посетите localhost:8080/userInfo/selectById еще раз


Пятнадцать: Оптимизация

оптимизировать один: Разрешения не могут оставаться без изменений.Когда нам нужно изменить разрешения, нам нужно вручную изменить файл конфигурации широ, что явно неразумно. Лучший способ - хранить разрешения в базе данных и модифицировать базу данных при изменении

1: Создайте таблицу ресурсов URL и добавьте данные

CREATE TABLE `sys_permission_init` (
  `id` varchar(255) NOT NULL,
  `url` varchar(255) DEFAULT NULL COMMENT '程序对应url地址',
  `permission_init` varchar(255) DEFAULT NULL COMMENT '对应shiro权限',
  `sort` int(100) DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `sys_permission_init` VALUES ('1', '/userInfo/login', 'anon', '1');
INSERT INTO `sys_permission_init` VALUES ('2', '/userInfo/selectAll', 'anon', '2');
INSERT INTO `sys_permission_init` VALUES ('3', '/logout', 'anon', '3');
INSERT INTO `sys_permission_init` VALUES ('4', '/**', 'authc', '0');
INSERT INTO `sys_permission_init` VALUES ('5', '/userInfo/selectAlla', 'authc, roles[admin]', '6');
INSERT INTO `sys_permission_init` VALUES ('6', '/sysPermissionInit/aaa', 'anon', '5');

Здесь мы должны позволить'/**'всегда в конце, поэтому нужно добавитьsortСортировать

2: Генерируем маппер, дао, сервис, контроллер в соответствии с инструментом и добавляем нужные нам методы запроса

SysPermissionInitMapper.xml

<sql id="Base_Column_List">
  id, url, permission_init, sort
</sql>

<select id="selectAllOrderBySort" resultMap="BaseResultMap">
  SELECT
  <include refid="Base_Column_List"/>
  from sys_permission_init
  order by sort desc
</select>

SysPermissionInitMapper.java

List<SysPermissionInit> selectAllOrderBySort();

3: изменитьShiroConfigurer.java

Изменено следующим образом

package com.example.demo.core.configurer;

import com.example.demo.core.shiro.CustomRealm;
import com.example.demo.model.SysPermissionInit;
import com.example.demo.service.SysPermissionInitService;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.List;

@Configuration
public class ShiroConfigurer {

    @Resource
    private SysPermissionInitService sysPermissionInitService;

    /**
     * 注入自定义的realm,告诉shiro如何获取用户信息来做登录或权限控制
     */
    @Bean
    public Realm realm() {
        return new CustomRealm();
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        creator.setUsePrefix(true);
        return creator;
    }

    /**
     * 这里统一做鉴权,即判断哪些请求路径需要用户登录,哪些请求路径不需要用户登录
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        List<SysPermissionInit> list = sysPermissionInitService.selectAllOrderBySort();
        for(int i = 0,length = list.size();i<length;i++){
            SysPermissionInit sysPermissionInit = list.get(i);
            chain.addPathDefinition(sysPermissionInit.getUrl(), sysPermissionInit.getPermissionInit());
        }
        return chain;
    }
}

4: тест

Введите localhost:8080/userInfo/selectById


затем войдите в систему

Посетите localhost:8080/userInfo/selectById еще раз, и мы увидим, что shiro вступил в силу.


Изменить права доступа пользователя к базе данных, перезапустить, войти снова

UPDATE sys_permission_init
SET permission_init = 'authc, roles[cw]'
WHERE
	id = 5

Посетите localhost:8080/userInfo/selectById еще раз

Оптимизация вторая: Каждый раз, когда мы изменяем информацию о разрешениях, нам нужно перезапускать сервер, что явно неразумно.

1: Создать ШироСервис

package com.example.demo.service;

import java.util.Map;

/**
 * shiro 动态更新权限
 */
public interface ShiroService {

    Map<String, String> loadFilterChainDefinitions();

    /**
     * 动态修改权限
     */
    void updatePermission();
}

2: Создать ШироСервисИмпл

package com.example.demo.service.impl;

import com.example.demo.model.SysPermissionInit;
import com.example.demo.service.ShiroService;
import com.example.demo.service.SysPermissionInitService;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ShiroServiceImpl implements ShiroService {

    @Autowired
    ShiroFilterFactoryBean shiroFilterFactoryBean;

    @Autowired
    SysPermissionInitService sysPermissionInitService;

    /**
     * 初始化权限
     */
    @Override
    public Map<String, String> loadFilterChainDefinitions() {
        // 权限控制map.从数据库获取
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        List<SysPermissionInit> list = sysPermissionInitService.selectAllOrderBySort();
        for (SysPermissionInit sysPermissionInit : list) {
            filterChainDefinitionMap.put(sysPermissionInit.getUrl(),
                    sysPermissionInit.getPermissionInit());
        }
        return filterChainDefinitionMap;
    }


    /**
     * 重新加载权限
     */
    @Override
    public void updatePermission() {
        synchronized (shiroFilterFactoryBean) {
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                        .getObject();
            } catch (Exception e) {
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
                    .getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
                    .getFilterChainManager();
            // 清空老的权限控制
            manager.getFilterChains().clear();
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());
            // 重新构建生成
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
            System.out.println("更新权限成功!!");
        }
    }
}

3: Изменить shiroUtilsController

Добавьте следующий код

/**
 * @Description: 重新加载shiro权限
 * @throws Exception
 */
@PostMapping("/updatePermission")
public void updatePermission() throws Exception {
    shiroService.updatePermission();
}

4: тест

Введите localhost:8080/userInfo/selectById


разрешение на редактирование

UPDATE sys_permission_init
SET permission_init = 'authc, roles[cw1]'
WHERE
id = 5

Введите localhost:8080/shiroUtils/updatePermission для перезагрузки разрешений.

Примечание. На данный момент мы не модифицировали никакую часть программы и не перезапускали сервер.

Введите localhost:8080/userInfo/selectById


успех!


Дополнение: Генерация зашифрованного пароля пользователя

Файл находится в core→utils→test.java

public static void main(String []ages ){
    //加密方式
    String hashAlgorithmName = "md5";
    //原密码
    String credentials = "123456";
    //加密次数
    int hashIterations = 1024;
    //加密盐值,大家可以用生成字符串的方法
    String hash = "wxKYXuTPST5SG0jMQzVPsg==";
    ByteSource credentialsSalt = ByteSource.Util.bytes(hash);
    String password = new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, hashIterations).toHex();
    System.out.println(password);
}


адрес проекта

Адрес облака кода:git ee.com/bean также/no SPR…

Адрес гитхаба:GitHub.com/my bean also/no s…

Писать статьи непросто, если это вам поможет, нажмите звездочку

конец

Завершена функция добавления интерфейса защиты разрешений широ, а последующие функции будут обновляться одна за другой.Если у вас есть какие-либо вопросы, свяжитесь со мной по адресу mr_beany@163.com. Также попросите руководства у всех великих богов, спасибо всем.