前言:小刘复习到了shiro,就和spring一起用起来。验证学到的结果。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
主要功能
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
在这个项目demo中,我将复习回顾spring+shiro的只是内容
Apache Shiro 是一个强大易用的 Java 安全框架,提供了提供了认证、授权、加密和会话管理等功能,等功能,
shiro工作流程--》
Application Code--------->Subject------>Shiro SecurityManager------>Realm
Sucject:主体
SecurityManager:安全管理器---shiro的核心
Realm:域,也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。
credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。
最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。
另外两个相关的概念是之前提到的 Subject 及 Realm,分别是主体及验证主体的数据源。
登录、、、
//3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
//4、登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//5、身份验证失败
}
会话存储/持久化
Shiro 提供 SessionDAO 用于会话的 CRUD,即 DAO(Data Access Object)模式实现: ---继承它或子类接口
------注意,realm中的授权是在什么时候进行授权呢?
经断点调试--只有用到的时候才会。
例如--需要某个角色,权限才可以访问该路径时候对授权,进行查看该用户是否有该权限
UserRealm 父类 AuthorizingRealm 将获取 Subject 相关信息分成两步:获取身份验证信息(doGetAuthenticationInfo)及授权信息(doGetAuthorizationInfo);
doGetAuthenticationInfo 获取身份验证相关信息
doGetAuthorizationInfo 获取授权信息
项目依赖:
<!--shiro的jar包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--开启spring+shiro的注解包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.0</version>
</dependency>
mvc.xml中
开启shiro的注解:
<aop:config proxy-target-class="true"></aop:config>
<bean class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
spring-shiro.xml
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator"
class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话DAO -->
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="180000"/>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<bean id="realm" class="com.example.realm.MyRealm"></bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"/>
<property name="sessionManager" ref="sessionManager"/>
<!--<property name="cacheManager" ref="cacheManager"/>-->
</bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.jsp"/><!--需要登录后才可以访问的请求路径,失败后,转到-->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<property name="filterChainDefinitions">
<value>
/index1=anon <!--没有参数,表示可以匿名使用。-->
/admin/haha=authc <!--身份认证成功-->
</value>
</property>
</bean>
public class MyRealm extends AuthorizingRealm {
@Autowired
AdminService adminService;
/***
* 授权-----主要是授予管理员角色和权限
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
//这是shiro设置权限的的类--设置角色和权限
// ----需要通过用户身份查明用户的权限
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//角色集合:
Set<String> roleSet = adminService.roles(username);
authorizationInfo.setRoles(roleSet);
//权限集合
Set<String> permissionSet = adminService.permissions(username);
authorizationInfo.setStringPermissions(permissionSet);
System.out.println("角色"+roleSet);
System.out.println("权限"+permissionSet);
return authorizationInfo;
}
/**
* 认证
*
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
AdminUser adminUser = adminService.findByUserName(username);
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以在此判断或自定义实现
if (adminUser!=null){
/**Object principal 身份---唯一即可?可以是任何东西,如用户名、邮箱等,唯一即可。也可以是类
* Object hashedCredentials, 凭证
* ByteSource credentialsSalt 加密算法
* String realmName realm 的名称
*
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(adminUser.getUsername(), adminUser.getPassword(), null, getName());
return info;
}
return null;
}
登录:
@RequestMapping(value = "/login")
@ResponseBody
public String login(String username,String password){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);//网站记住我
try {
subject.login(token);
return "登录成功";
} catch (AuthenticationException e) {
e.printStackTrace();
return "登录失败,用户名或者密码错误";
}
}
shiro常用的注解:
@RequiresAuthentication//这个路径,身份必须验证后才可以访问这个路径
@RequiresGuest//只能是未认证登录的游客才可以进行访问
@RequiresRoles(value = {"老板"})
@RequiresPermissions(value = {"用户管理","商品管理"})
web.xml中配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 设置contextConfigLocation的参数,指明路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring*.xml</param-value>
</context-param>
<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>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
如有不解,请加java爱好群大家交流:852665736;群里都是热心好客的小伙伴,大家一同进步。
无偿免费分享源码以及技术和面试文档,更多优秀精致的源码技术栈分享请关注微信公众号:gh_817962068649