Authentication :身份认证/登录,验证用户是不是拥有相应的身份;
Authorization : 授权,即权限验证,验证即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
Applicationcontext-shiro.xml代码 收藏代码
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<description>Shiro 配置</description>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.jsp" />
<property name="successUrl" value="/index.jsp" />
<property name="unauthorizedUrl" value="/login.do" />
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/login.do = anon
/** = authc
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm-->
<property name="realm" ref="monitorRealm" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!--自定义Realm 继承自AuthorizingRealm-->
<bean id="monitorRealm" class="***module.system.security.MonitorRealm"></bean>
<!-- securityManager -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
将shiro的配置文件引入到web.xml中:
并在web.xml中加入如下代码:
Xml代码 收藏代码
<!-- Shiro Security filter -->
<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>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
实现自己的Realm
Java代码 收藏代码
@Service("monitorRealm")
public class MonitorRealm extends AuthorizingRealm {
@Autowired UserService userService;
@Autowired RoleService roleService;
@Autowired LoginLogService loginLogService;
public MonitorRealm(){
super();
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
/*这里编写授权代码*/
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
/*这里编写认证代码*/
}
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
clearCachedAuthorizationInfo(principals);
}
}
登录时的代码示例:
Java代码 收藏代码
Subject currentUser = SecurityUtils.getSubject();
if(!currentUser.isAuthenticated()){
UsernamePasswordToken token;
if(null == rememberMe)
token = new UsernamePasswordToken(user.getUsername(), EncryptUtils.encodeMD5String(user.getPassword()),false,request.getRemoteAddr());
else token = new UsernamePasswordToken(user.getUsername(), EncryptUtils.encodeMD5String(user.getPassword()), true, request.getRemoteAddr());
try {
currentUser.login(token);
} catch ( AuthenticationException ae ) {
request.setAttribute("message", "用户名或密码错误!");
return "login";
}
}
执行currentUser.login(token);这句代码时,shiro会自动调用用户实现的Realm的doGetAuthenticationInfo进行身份认证。
登出时的代码示例:
Java代码 收藏代码
Subject currentUser = SecurityUtils.getSubject();
if (currentUser != null) {
currentUser.logout();
}
HttpSession session = request.getSession(false);
if( session != null ) {
session.invalidate();
}
return "login";
在对用户(角色)进行授权时会执行Realm里的doGetAuthorizationInfo方法。
————————————————————————————————————————————
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
</web-app>
-------------------------------------------------------------------------
servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/view directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.bicycle.web" />
<tx:annotation-driven/>
<!-- a PlatformTransactionManager is still required -->
<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource">
<beans:ref bean="dataSource"/>
</beans:property>
</beans:bean>
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<beans:property name="defaultEncoding" value="UTF-8"/> <!-- 默认编码 -->
<beans:property name="maxInMemorySize" value="4096"/> <!-- 最大内存大小 -->
<beans:property name="maxUploadSize" value="-1"/> <!-- 最大文件大小,-1为无限制 -->
</beans:bean>
<!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->
<!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,而且此时还没有进入到Controller方法中 -->
<beans:bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<beans:property name="exceptionMappings">
<beans:props>
<!-- 遇到MaxUploadSizeExceededException异常时,自动跳转到/WEB-INF/views/fileError.jsp页面 -->
<beans:prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">fileError</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<beans:bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<beans:property name="proxyTargetClass" value="true" />
</beans:bean>
<beans:bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<beans:property name="securityManager">
<beans:ref bean="securityManager"/>
</beans:property>
</beans:bean>
<!-- Enables the Spring Task @Scheduled programming model -->
<task:executor id="executor" pool-size="5" />
<task:scheduler id="scheduler" pool-size="10" />
<task:annotation-driven executor="executor" scheduler="scheduler" />
<!-- 加载cron 系统配置 -->
<context:property-placeholder location="classpath:project.properties"/>
</beans:beans>
---------------------------------------------------------------------
root-context.xml:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:property-placeholder location="classpath:project.properties"/>
<context:property-placeholder location="classpath:sql.properties"/>
<import resource="classpath*:datasource.xml"/>
<!-- Security Configuration / Apache Shiro -->
<import resource="shiro-context.xml"/>
</beans>
-----------------------------------------------------------------------
<注:springboot配置之一>
shiro-context.xml:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/main"/>
<property name="unauthorizedUrl" value="/unauthorized"/>
<property name="filterChainDefinitions">
<value>
/login = anon
/resources/** = anon
/** = authc
</value>
</property>
</bean>
<bean id="sampleRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="sampleRealm" />
<property name="cacheManager" ref="shiroCacheManager"/>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<import resource="classpath*:shiro-realm.xml"/>
</beans>
----------------------------------------------------------------
<注:springboot配置之二>
shiro-realm.xml:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="dataSource" ref="dataSource"/>
<property name="authenticationQuery" value="SELECT passphrase, CONCAT('${shiro.applicationSalt}', ':', salt) as salt FROM users where userid = ?"/>
<property name="userRolesQuery" value="SELECT name FROM roles LEFT JOIN user_roles ON roles.id = user_roles.role_id LEFT JOIN users ON user_roles.user_id = users.id WHERE userid = ?"/>
<property name="permissionsQuery" value="SELECT permission FROM role_permissions LEFT JOIN roles ON role_permissions.role_id = roles.id WHERE roles.name = ?"/>
<property name="permissionsLookupEnabled" value="true"/>
<property name="saltStyle" value="COLUMN"/>
<property name="credentialsMatcher" ref="sha512Matcher"/>
</bean>
</beans>
----------------------------------------------------------------
Controller部分:
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(@Validated UserFormBean loginUser, BindingResult result, HttpServletRequest request, Model model){
String loginMessage = null;
if(result.hasErrors()){
loginMessage = "Username or password cannot be empty, please enter again!";
model.addAttribute("loginMessage", loginMessage);
return "Bicycle/login";
}
String username = loginUser.getName();
String userpwd = loginUser.getPassword();
UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
Subject currentUser = SecurityUtils.getSubject();
try{
currentUser.login(token);
SecurityUtils.getSubject().getSession().setTimeout(21600000);
}catch(UnknownAccountException e){
//不认识的账户
loginMessage = "Username or password error, please check!";
model.addAttribute("loginMessage", loginMessage);
return "Bicycle/login";
}catch(IncorrectCredentialsException e){
//密码错误
loginMessage = "Username or password error, please check!";
model.addAttribute("loginMessage", loginMessage);
return "Bicycle/login";
}catch(AuthenticationException e){
//登录出错的总异常类
loginMessage = "Validation exceptions: ";
model.addAttribute("loginMessage", loginMessage + e.getMessage());
return "Bicycle/login";
}
return "redirect:/main";
}
----------------------------------------
AuthenticationException 异常类的子类有:
UsernameNotFoundException 用户找不到
BadCredentialsException 坏的凭据
AccountStatusException 用户状态异常它包含如下子类
AccountExpiredException 账户过期
LockedException
账户锁定
DisabledException
账户不可用
CredentialsExpiredException
证书过期
-----------------------------------------
pom.xml添加的依赖:
<properties>
<org.apache.shiro-version>1.2.6</org.apache.shiro-version>
</properties>
<dependencies>
<!-- security -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
</dependencies>