标签:tab onclick view click 项目问题 efault 配置文件 信息 判断
? Apache Shiro是Java的一个安全框架。它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。 Apache Shiro相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,能更简单的解决项目问题就好了。Shiro不仅可以用在JavaSE环境,也可以用在JavaEE环境帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。而且Shiro的API也是非常简单。
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager :安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm :域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
也就是说对于我们而言,最简单的一个Shiro应用:
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。
从以上也可以看出,Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。
<!--shiro--> <!--shiro和spring整合--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <!--shiro核心包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.3.2</version> </dependency> <!--shiro缓存包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.3.2</version> </dependency>
<!-- Shiro Security filter filter-name这个名字的值将来还会在spring中用到--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <description>Shiro与Spring整合</description> <!--使用redis作为缓存的配置--> <!--<bean id="redisManager" class="org.crazycake.shiro.RedisManager"> <property name="host" value="127.0.0.1:6379"></property> </bean> <bean id="cacheManager" class="org.crazycake.shiro.RedisCacheManager"> <property name="redisManager" ref="redisManager"></property> </bean>--> <!--<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean>--> <!--使用内存作为缓存的配置:ehcache--> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:cache/ehcache-shiro.xml"></property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="authRealm"/><!-- 引用自定义的realm --> <property name="cacheManager" ref="cacheManager"></property> </bean> <!-- 自定义Realm域的编写 --> <bean id="authRealm" class="cn.dintalk.web.shiro.AuthRealm"> <!-- 注入自定义的密码比较器--> <property name="credentialsMatcher" ref="customerCredentialsMatcher"></property> </bean> <!-- 自定义的密码比较器 --> <bean id="customerCredentialsMatcher" class="cn.dintalk.web.shiro.CustomCredentialsMatcher"></bean> <!-- filter-name这个名字的值来自于web.xml中filter的名字 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <!--登录页面 --> <property name="loginUrl" value="/login.jsp"></property> <!-- 无权访问页面 --> <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <property name="filterChainDefinitions"> <!-- /**代表下面的多级目录也过滤 --> <value> <!--权限访问规则 /company/list.do = perms["企业管理"]--> <!--匿名访问规则--> /index.jsp* = anon /login.jsp* = anon /login* = anon /logout* = anon /css/** = anon /img/** = anon /plugins/** = anon /make/** = anon <!--认证访问规则--> /** = authc,user /*.* = authc </value> </property> </bean> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- 生成代理,通过代理进行控制 depends-on:指定当前类的创建时必须在指定bean的id后面创建 lifecycleBeanPostProcessor的创建要在DefaultAdvisorAutoProxyCreator之前 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true"/> </bean> <!-- 安全管理器 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!--动态代理的代理规则:使用目标类的子类创建代理对象,使用cglib--> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
@RequiresPermissions("企业管理") @RequestMapping(value = "/list",name = "查看企业列表") public String findAll(@RequestParam(defaultValue = "1") int page,@RequestParam(defaultValue = "5") int size) { PageInfo pageInfo = companyService.findPageByHelper(page, size); request.setAttribute("page", pageInfo); return "company/company-list"; }
<?xml version="1.0" encoding="UTF-8"?> <ehcache updateCheck="false" name="shiroCache"> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> </ehcache>
/** * 自定义realm域,进行认证:用户名和密码的校验 * 和授权 :获取用户的权限和每次方法的权限鉴定(鉴权) * * @author Mr.song * @date 2019/05/11 16:27 */ public class AuthRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 授权的方法 还可以配合标签进行鉴权 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.取出认证成功的用户(这里就是User,所以直接强转) User user = (User)principalCollection.getPrimaryPrincipal(); //2.查询该用户具备的功能 List<Module> moduleList = userService.findUserMenus(user.getId()); //3.将功能模块的名称发放入set集合,将来通过功能名称与配置的访问规则匹配 Set<String> moduleSet = new HashSet<>(); for (Module module : moduleList) { moduleSet.add(module.getName()); } //4.创建返回值对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //5.按照返回值要求填充对象并返回 info.setStringPermissions(moduleSet); return info; } /** * 认证的方法,使用用户名和密码到数据库进行查询 * @param authenticationToken 令牌(及包装后的登录信息) * @return 若此方法返回null,则subject的login方法就会抛出异常 * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.把参数转成UsernamePasswordToken UsernamePasswordToken uToken = (UsernamePasswordToken)authenticationToken; //2.获取用户输入的登录名和密码 String email = uToken.getUsername(); String password = new String(uToken.getPassword(),0,uToken.getPassword().length); //3.使用邮箱到数据库查询 User user = userService.findByEmail(email); //4.当user不为null时,按照要求创建返回值 if (user != null){ //按照返回值要求创建对象即可,构造方法内部会调用我们自定义的密码比较器 //构造函数:传三个参数,当前用户,当前用户数据库密码,及当前的realm域的名称 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName()); return info; } //没查到null,直接返回null,controller捕获后处理 return null; } }
/** * 自定义的密码校验器 * * @author Mr.song * @date 2019/05/11 16:35 */ public class CustomCredentialsMatcher extends SimpleCredentialsMatcher { /** * 做密码比较 * @param token 认证令牌: 即包装过后的登录信息(登录名和密码) * @param info 认证信息: 存着密文密码(即数据库中的密码) * @return */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //1.取出明文密码 UsernamePasswordToken uToken = (UsernamePasswordToken)token; String email = uToken.getUsername(); String password = new String(uToken.getPassword(),0,uToken.getPassword().length); //2.取出密文密码 String dbPassword = (String) info.getCredentials(); //3.将明文密码加密后进行比较 String md5Password = Encrypt.md5(password,email); //4.返回比较结果,返回false时会抛出异常,处理器捕获进行处理 return md5Password.equals(dbPassword); } }
//登录 @RequestMapping(value = "/login",name = "登录") public String login(String email,String password){ try { //1.获取主体信息 Subject subject = SecurityUtils.getSubject(); //2.获取令牌,将登录信息套个壳子 UsernamePasswordToken uToken = new UsernamePasswordToken(email,password); //3.调用subject的登录方法,将uToken交给shiro的核心 subject.login(uToken); //4.获取认证的结果(这里其实就是User对象) User user = (User)subject.getPrincipal();//若没有获取到,shiro会报错,即认证失败 //5.匹配成功,可以登录( 根据用户的角色信息动态展示菜单 ) session.setAttribute("user",user); //6.查询当前用户角色的权限菜单 List<Module> moduleList = userService.findUserMenus(user.getId()); session.setAttribute("modules",moduleList); return "home/main"; } catch (AuthenticationException e) {// 认证失败 //无此用户,或密码不对 request.setAttribute("error","邮箱或密码不匹配! 请重试"); return "forward:/login.jsp"; } }
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<shiro:hasPermission name="新建企业"> <button type="button" class="btn btn-default" title="新建" onclick=‘location.href="${ctx}/company/toAdd.do"‘> <i class="fa fa-file-o"></i> 新建 </button> </shiro:hasPermission>
标签名称 | 标签条件(均是显示标签内容) |
---|---|
shiro:authenticated | 登录之后 |
shiro:notAuthenticated | 不在登录状态时 |
shiro:guest | 用户在没有RememberMe时 |
shiro:user | 用户在RememberMe时 |
<shiro:hasAnyRoles name="abc,123" > | 在有abc或者123角色时 |
<shiro:hasRole name="abc"> | 拥有角色abc |
<shiro:lacksRole name="abc"> | 没有角色abc |
<shiro:hasPermission name="abc"> | 拥有权限资源abc |
<shiro:lacksPermission name="abc"> | 没有abc权限资源 |
shiro:principal | 默认显示用户名称 |
关注微信公众号,随时随地学习
标签:tab onclick view click 项目问题 efault 配置文件 信息 判断
原文地址:https://www.cnblogs.com/dintalk/p/10852343.html