shiro 使用

叁歲伎倆 2021-09-28 15:22 460阅读 0赞
  • shiro 核心架构

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BpZ3FoZg_size_16_color_FFFFFF_t_70

  1. Subject(主体):与软件交互的一个特定的实体(用户、第三方服务等)。
  2. SecurityManager(安全管理器) :Shiro 的核心,用来协调管理组件工作。
  3. Authenticator(认证管理器):负责执行认证操作
  4. Authorizer(授权管理器):负责授权检测
  5. SessionManager(会话管理):负责创建并管理用户 Session 生命周期,提供一个强有力的 Session 体验。
  6. SessionDAO:代表 SessionManager 执行 Session 持久(CRUD)动作,它允许任何存储的数据挂接到 session 管理基础上。
  7. CacheManager(缓存管理器):提供创建缓存实例和管理缓存生命周期的功能
  8. Cryptography(加密管理器):提供了加密方式的设计及管理。
  9. Realms(领域对象):是shiro和你的应用程序安全数据之间的桥梁。
  • shiro 认证

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BpZ3FoZg_size_16_color_FFFFFF_t_70 1

  1. 系统调用subject的login方法将用户信息提交给SecurityManager
  2. SecurityManager将认证操作委托给认证器对象Authenticator
  3. Authenticator将用户输入的身份信息传递给Realm。
  4. Realm访问数据库获取用户信息然后对信息进行封装并返回。
  5. Authenticator 对realm返回的信息进行身份认证。
  • shiro 授权

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BpZ3FoZg_size_16_color_FFFFFF_t_70 2

  1. 系统调用subject相关方法将用户信息(例如isPermitted)递交给SecurityManager
  2. SecurityManager将权限检测操作委托给Authorizer对象,Authorizer将用户信息委托给realm.
  3. Realm访问数据库获取用户权限信息并封装。
  4. Authorizer对用户授权信息进行判定。

  • 添加依赖


    org.apache.shiro
    shiro-spring
    1.3.2
  • web.xml

    1. <filter>
    2. <filter-name>shiroFilter</filter-name>
    3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    4. <init-param>
    5. <param-name>targetBeanName</param-name>
    6. <param-value>shiroFilterFactory</param-value>
    7. </init-param>
    8. </filter>
    9. <filter-mapping>
    10. <filter-name>shiroFilter</filter-name>
    11. <url-pattern>/*</url-pattern>
    12. </filter-mapping>
  • spring-shiro.xml

    <?xml version=”1.0” encoding=”UTF-8”?>

    1. <!-- shiro过滤与认证 -->
    2. <bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    3. <property name="SecurityManager" ref="securityManager" />
    4. <property name="LoginUrl" value="/doLoginUI.do" />
    5. <!-- 设置请求过滤规则 -->
    6. <property name="FilterChainDefinitionMap">
    7. <map>
    8. <!--anon 匿名可访问,authc 必须认证,假如添加了记住我功能以后需要设置为user -->
    9. <entry key="/bower_components/**" value="anon" />
    10. <entry key="/build/**" value="anon" />
    11. <entry key="/dist/**" value="anon" />
    12. <entry key="/plugins/**" value="anon" />
    13. <entry key="/user/doLogin.do" value="anon" />
    14. <entry key="/doLogout.do" value="logout" />
    15. <!--<entry key="/**" value="authc" /> 必须认证 -->
    16. <entry key="/**" value="user" />
    17. </map>
    18. </property>
    19. </bean>
    20. <!-- 安全管理器 -->
    21. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    22. <!-- 认证与授权,在 ShiroUserRealm 类中重写各自方法-->
    23. <property name="Realm" ref="shiroUserRealm" />
    24. <!-- shiro缓存 -->
    25. <property name="CacheManager" ref="cacheManager" />
    26. <!-- remember me -->
    27. <property name="RememberMeManager" ref="rememberMeManager" />
    28. <!-- session -->
    29. <property name="sessionManager" ref="sessionManager"/>
    30. </bean>
    31. <!-- shiro授权 -->
    32. <!-- 授权属性的Advisor配置 -->
    33. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    34. <property name="SecurityManager" ref="securityManager" />
    35. </bean>
    36. <!-- 配置bean对象的生命周期管理 -->
    37. <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
    38. <!-- 通过此配置要为目标业务对象创建代理对象 -->
    39. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
    40. depends-on="lifecycleBeanPostProcessor">
    41. </bean>
  1. <!-- shiro缓存 -->
  2. <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
  3. <!-- remember me -->
  4. <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
  5. <constructor-arg value="rememberMe" />
  6. <!-- 7天,采用spring el表达式来计算,方便修改 -->
  7. <property name="maxAge" value="#{7 * 24 * 60 * 60}" />
  8. </bean>
  9. <bean id="rememberMeManager"
  10. class="org.apache.shiro.web.mgt.CookieRememberMeManager">
  11. <property name="cookie" ref="rememberMeCookie" />
  12. </bean>
  13. <!-- shiro结合Session会话管理器 start -->
  14. <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
  15. <!-- session的失效时长,单位毫秒 1小时: 3600000, itzixi站点设置以 6小时 为主:21600000 -->
  16. <!-- 设置全局会话超时时间,默认30分钟,即如果30分钟内没有访问会话将过期 1800000 -->
  17. <property name="globalSessionTimeout" value="21600000" />
  18. <!-- 删除失效的session -->
  19. <property name="deleteInvalidSessions" value="true" />
  20. <!-- 是否开启会话验证器,默认是开启的 -->
  21. <property name="sessionValidationSchedulerEnabled" value="true" />
  22. </bean>
  23. </beans>
  • service 层新建类 ShiroUserRealm 继承AuthorizingRealm 接口,重写3个方法

    @Service
    public class ShiroUserRealm extends AuthorizingRealm {

    1. @Autowired
    2. private SysUserDao sysUserDao;
    3. @Autowired
    4. private SysUserRoleDao sysUserRoleDao;
    5. @Autowired
    6. private SysRoleMenuDao sysRoleMenuDao;
    7. @Autowired
    8. private SysMenuDao sysMenuDao;
    9. /** 通过此方法完成授权信息的获取及封装,在service 层的方法使用@RequiresPermissions("sys:user:valid")来允许拥有sys:user:valid 权限的用户调用方法 */
    10. @Override
    11. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    12. // 1.获取登录用户信息,例如用户id
    13. SysUser user = (SysUser) principals.getPrimaryPrincipal();
    14. Integer userId = user.getId();
    15. // 2.基于用户id获取用户拥有的角色(sys_user_roles)
    16. List<Integer> roleIds = sysUserRoleDao.findRoleIdsByUserId(userId);
    17. if (roleIds == null || roleIds.size() == 0)
    18. throw new AuthorizationException();
    19. // 3.基于角色id获取菜单id(sys_role_menus)
    20. Integer[] array = {};
    21. List<Integer> menuIds = sysRoleMenuDao.findMenuIdsByRoleIds(roleIds.toArray(array));
    22. if (menuIds == null || menuIds.size() == 0)
    23. throw new AuthorizationException();
    24. // 4.基于菜单id获取权限标识(sys_menus)
    25. List<String> permissions = sysMenuDao.findPermissions(menuIds.toArray(array));
    26. // 5.对权限标识信息进行封装并返回
    27. Set<String> set = new HashSet<>();
    28. for (String per : permissions) {
    29. if (!StringUtils.isEmpty(per)) {
    30. set.add(per);
    31. }
    32. }
    33. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    34. info.setStringPermissions(set);
    35. return info;// 返回给授权管理器
    36. }
    37. /**
    38. * 设置凭证匹配器(与用户添加操作使用相同的加密算法)
    39. */
    40. @Override
    41. public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
    42. // 构建凭证匹配对象
    43. HashedCredentialsMatcher cMatcher = new HashedCredentialsMatcher();
    44. // 设置加密算法
    45. cMatcher.setHashAlgorithmName("MD5");
    46. // 设置加密次数
    47. cMatcher.setHashIterations(1);
    48. super.setCredentialsMatcher(cMatcher);
    49. }
    50. /**
    51. * 通过此方法完成认证数据的获取及封装,系统 底层会将认证数据传递认证管理器,由认证 管理器完成认证操作。
    52. */
    53. @Override
    54. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    55. // 1.获取用户名(用户页面输入)
    56. UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    57. String username = upToken.getUsername();
    58. // 2.基于用户名查询用户信息
    59. SysUser user = sysUserDao.findUserByUserName(username);
    60. // 3.判定用户是否存在
    61. if (user == null)
    62. throw new UnknownAccountException("账户不存在");
    63. // 4.判定用户是否已被禁用。
    64. if (user.getValid() == 0)
    65. throw new LockedAccountException();
    66. // 5.封装用户信息
    67. ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
    68. // 记住:构建什么对象要看方法的返回值
    69. SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, // principal (身份)
    70. user.getPassword(), // hashedCredentials,token 存有前端传来的密码,这里是数据库取的密码
    71. credentialsSalt, // credentialsSalt,每个账号的盐值都不同,所以要从数据库获取账户的盐值
    72. getName());// realName
    73. // 6.返回封装结果
    74. return info;// 返回值会传递给认证管理器(后续
    75. // 认证管理器会通过此信息完成认证操作)
    76. }

    }

  • 业务Service 层

保存用户,保存密码代码片段

  1. // 将数据写入数据库,保存密码部分
  2. String salt = UUID.randomUUID().toString();
  3. entity.setSalt(salt);
  4. // 加密
  5. SimpleHash sHash = new SimpleHash("MD5", entity.getPassword(), salt, 1);
  6. entity.setPassword(sHash.toHex());

更新密码

  1. @Override
  2. public int updatePassword(String password, String newPassword, String cfgPassword) {
  3. // 1.判定新密码与密码确认是否相同
  4. if (StringUtils.isEmpty(newPassword))
  5. throw new IllegalArgumentException("新密码不能为空");
  6. if (StringUtils.isEmpty(cfgPassword))
  7. throw new IllegalArgumentException("确认密码不能为空");
  8. if (!newPassword.equals(cfgPassword))
  9. throw new IllegalArgumentException("两次输入的密码不相等");
  10. // 2.判定原密码是否正确
  11. if (StringUtils.isEmpty(password))
  12. throw new IllegalArgumentException("原密码不能为空");
  13. // 获取登陆用户
  14. SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();//获取的是当前登陆用户
  15. SimpleHash sh = new SimpleHash("MD5", password, user.getSalt(), 1);
  16. if (!user.getPassword().equals(sh.toHex()))
  17. throw new IllegalArgumentException("原密码不正确");
  18. // 3.对新密码进行加密
  19. String salt = UUID.randomUUID().toString();
  20. sh = new SimpleHash("MD5", newPassword, salt, 1);
  21. // 4.将新密码加密以后的结果更新到数据库
  22. int rows = sysUserDao.updatePassword(sh.toHex(), salt, user.getId());
  23. if (rows == 0)
  24. throw new ServiceException("修改失败");
  25. return rows;
  26. }
  • Controller 层

    @RequestMapping(“/doLogin”)

    1. @ResponseBody
    2. public ReturnJson doLogin(boolean isRememberMe, String username, String password) {
    3. // 1.获取Subject对象
    4. Subject subject = SecurityUtils.getSubject();
    5. // 2.通过Subject提交用户信息,交给shiro框架进行认证操作
    6. // 2.1对用户进行封装
    7. UsernamePasswordToken token = new UsernamePasswordToken(
    8. username, // 身份信息
    9. password);// 凭证信息
    10. if (isRememberMe) {
    11. token.setRememberMe(true);
    12. }
    13. // 2.2对用户信息进行身份认证
    14. subject.login(token);
    15. // 分析:
    16. // 1)token会传给shiro的SecurityManager
    17. // 2)SecurityManager将token传递给认证管理器
    18. // 3)认证管理器会将token传递给realm
    19. return new ReturnJson("login ok");
    20. }

  • 另java 类配置shiro

    package com.db.common.config;

    import java.util.LinkedHashMap;
    import java.util.Map;

    import org.apache.shiro.cache.CacheManager;
    import org.apache.shiro.cache.MemoryConstrainedCacheManager;
    import org.apache.shiro.mgt.RememberMeManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;

    import com.db.sys.service.realm.ShiroUserRealm;

    /* @Configuration 描述的bean一般为一个配置类 /
    @Configuration
    public class SpringShiroConfig {

    1. /**
    2. * @Bean一般用户描述方法,然后将方法的返回值 交给Spring管理,其中@Bean注解中的内容为Bean 对象的key。
    3. * @return
    4. */
    5. @Bean("securityManager")
    6. public SecurityManager newSecurityManager(ShiroUserRealm realm, CacheManager cacheManager,
    7. RememberMeManager rememberMeManager) {
    8. DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
    9. sm.setRealm(realm);
    10. sm.setCacheManager(cacheManager);
    11. sm.setRememberMeManager(rememberMeManager);
    12. sm.setSessionManager(newSessionManager());
    13. return sm;// 不是java.lang包中的SecurityManager
    14. }
    15. @Bean("shiroFilterFactory")
    16. public ShiroFilterFactoryBean newShiroFilterFactoryBean(SecurityManager securityManager) {
    17. // 1.构建ShiroFilterFactoryBean对象(负责创建ShiroFilter工厂对象)
    18. ShiroFilterFactoryBean fBean = new ShiroFilterFactoryBean();
    19. // 2.设置安全管理器
    20. fBean.setSecurityManager(securityManager);
    21. // 3.设置登录页面对应的url(非认证用户要跳转到此url对应的页面)
    22. fBean.setLoginUrl("/doLoginUI.do");
    23. // 4.设置过滤规则(哪些允许匿名访问,哪些需要认证访问)
    24. Map<String, String> filterMap = new LinkedHashMap<String, String>();
    25. filterMap.put("/bower_components/**", "anon");
    26. filterMap.put("/build/**", "anon");
    27. filterMap.put("/dist/**", "anon");
    28. filterMap.put("/plugins/**", "anon");
    29. filterMap.put("/user/doLogin.do", "anon");
    30. filterMap.put("/doLogout.do", "logout");
    31. filterMap.put("/**", "user");
    32. fBean.setFilterChainDefinitionMap(filterMap);
    33. return fBean;
    34. }
    35. // ==========================
    36. // @Bean注解没有指定名字时,默认bean的名字为方法名
    37. @Bean("lifecycleBeanPostProcessor")
    38. public LifecycleBeanPostProcessor newLifecycleBeanPostProcessor() {
    39. return new LifecycleBeanPostProcessor();
    40. }
    41. @DependsOn("lifecycleBeanPostProcessor")
    42. @Bean
    43. public DefaultAdvisorAutoProxyCreator DefaultAdvisorAutoProxyCreator() {
    44. return new DefaultAdvisorAutoProxyCreator();
    45. }
    46. @Bean
    47. public AuthorizationAttributeSourceAdvisor newAuthorizationAdvisor(SecurityManager securityManager) {
    48. AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    49. advisor.setSecurityManager(securityManager);
    50. return advisor;
    51. }
    52. // 配置缓存管理器(可以缓存用户的权限信息)
    53. @Bean
    54. public MemoryConstrainedCacheManager newCacheManager() {
    55. return new MemoryConstrainedCacheManager();
    56. }
    57. // 配置记住我
    58. @Bean
    59. public CookieRememberMeManager newCookieManager() {
    60. CookieRememberMeManager cookieManager = new CookieRememberMeManager();
    61. SimpleCookie cookie = new SimpleCookie("rememberMe");
    62. cookie.setMaxAge(24 * 7 * 60 * 60);
    63. cookieManager.setCookie(cookie);
    64. return cookieManager;
    65. }
    66. // 配置session管理器
    67. public DefaultWebSessionManager newSessionManager() {
    68. DefaultWebSessionManager sManager = new DefaultWebSessionManager();
    69. sManager.setGlobalSessionTimeout(21600000);
    70. sManager.setDeleteInvalidSessions(true);
    71. sManager.setSessionValidationSchedulerEnabled(true);
    72. return sManager;
    73. }

    }

    package com.db.common.config;
    import javax.servlet.FilterRegistration.Dynamic;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;

    import org.springframework.web.filter.DelegatingFilterProxy;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

    //替换web.xml (参考官网实现)
    public class AppWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{

    1. //其中前端控制器的配置是在父类的onStart方法实现的。
    2. @Override
    3. public void onStartup(ServletContext servletContext) throws ServletException {
    4. registerFilter(servletContext);
    5. super.onStartup(servletContext);
    6. }
    7. private void registerFilter(ServletContext servletContext) {
    8. Dynamic d = servletContext.addFilter("shiroFilter",
    9. DelegatingFilterProxy.class);
    10. d.setInitParameter("targetBeanName","shiroFilterFactory");
    11. d.addMappingForUrlPatterns(null,true,"/*");
    12. }
    13. /**
    14. * 配置@Service,@Repository
    15. */
    16. @Override
    17. protected Class<?>[] getRootConfigClasses() {
    18. return new Class[] {SpringRespositoryConfig.class,SpringServiceConfig.class};
    19. }
    20. /**加载spring mvc配置*/
    21. @Override
    22. protected Class<?>[] getServletConfigClasses() {
    23. return new Class[] {SpringWebConfig.class} ;
    24. }
    25. /**定义映射路径(spring mvc要处理哪些url)*/
    26. @Override
    27. protected String[] getServletMappings() {
    28. System.out.println("getServletMappings");
    29. return new String[] {"/"};
    30. }

    }

发表评论

表情:
评论列表 (有 0 条评论,460人围观)

还没有评论,来说两句吧...

相关阅读