1.导入jar包
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
2.web.xml
<!--加载spring-shiro.xml-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-shiro.xml</param-value>
</context-param>
<!--配置监听器就是servlet,随着web的应用启动而启动,只初始化一次-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置shiro的shiroFilter这个名字固定,与shiro.xml中的要一样
实际他是Filter的一个代理对象,sprong会到ioc容器中查找他对应的filter bean-->
<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>
3…spring-shiro.xml
<!--配置securityManger-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--<property name="cacheManager" ref="cacheManger"></property>-->
<property name="realm" ref="jdbcRealm"></property>
</bean>
<!--配置cacheManger-->
<!-- <bean id="cacheManger" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value=""></property>
</bean>-->
<!--配置Realm 这个必方法须实现接口来完成(继承了realm的接口) 即自定义realm-->
<bean id="jdbcRealm" class="sise.cn.realm.ShiroReam">
<!--配置凭证匹配器指定之后会自动把前台获取的密码变成MD5加密的-->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--指定加密算法的名字和方法-->
<property name="hashAlgorithmName" value="MD5"></property>
<!--指定加密的次数为1024次-->
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</bean>
<!--配置lifecycleBeanPostProcessor,可以自动的来调用配置在spring ioc容器中shiro的生命周期方法-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!--启用IOC容器中使用shiro的注解但必须在配置了lifecycleBeanPostProcessor之后才可以使用-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"></bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
<!--配置shiroFilter
id必须和web.xml中配置的DelegatingFilterProxy的name一致
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="/login.jsp"></property>
<property name="successUrl" value="/list.jsp"></property>
<property name="unauthorizedUrl" value="/unauthorized.jsp"></property>
<!--配置哪些页面需要受保护以及访问这些页面需要的权限
anon 可以被匿名访问
authc 必须认证(即登录)才可以访问
logout 登出的过滤器
-->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/shiro/login = anon
/shiro/logout = logout
/** = authc
</value>
</property>
</bean>
4.自定义Realm类
//单纯认证只要继承AuthenticatingRealm这个类就行
public class ShiroReam extends AuthenticatingRealm {
//token是currentUser.login(token)传过来的
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.把AuthenticationToken转化为UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.从UsernamePasswordToken中获取username
String username = upToken.getUsername();
//3.调用数据库,从数据库中查询username对应的用户记录
//根据用户名查找用户信息,调用dao
System.out.println("从数据库中获取username:" + username + "所对应的信息");
//4.若用户不存在则可以抛出异常,认证异常子类unknownAccountException异常
if("unknown".equals(username )){
throw new UnknownAccountException("用户不存在");
}
//5.根据用户信息的情况,决定是否需要抛出其他异常AuthenticationExceptin异常(锁定异常)
if("moster".equals(username)){
throw new LockedAccountException("用户被锁定");
}
//6.根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类: SimpleAuthenticationInfo
//一下信息是从数据库中获取的
//1.principal:认证的实体信息,可以是username,也可以是数据表对应的用户实体类对象
Object principal = username;
//2.credentials:从数据表中获取的密码
Object credentials = null;
if ("admin".equals(username)){
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}else if("user".equals(username)){
credentials = "098d2c478e9c11555ce2823231e02ec1";
}
//3.realmName:当前realm对象的name,调用父类getName()即可
String realmName = getName();
//4.盐值,因为用户名是唯一的所以拿用户名作为盐的原始值
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
/* SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials ,realmName);*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
return info;
}
//计算盐值
public static void main(String[] args) {
String hashAlgorithmName = "MD5";
Object credentials = "123456";
Object salt = ByteSource.Util.bytes("admin");
int hashIterations = 1024;
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
System.out.println(result);
}
}
5.controller
@Controller
@RequestMapping("/shiro")
public class ShiroHandler {
@RequestMapping("/login")
public String login(@RequestParam("username") String username,@RequestParam("password") String password){
//获取当前的Subject,调用securityutils.getSubject();
Subject currentUser = SecurityUtils.getSubject();
//没有认证
if(!currentUser.isAuthenticated()){
//把用户名和密码封装为usernamepasswordtoken对象
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
//rememberme
token.setRememberMe(true);
//执行登录
try {
//此步骤就是把token传到shiroream中去进行验证
currentUser.login(token);
} catch (AuthenticationException e) {
System.out.println("登录失败" + e.getMessage());
}
}
return "redirect:/list.jsp";
}
}
认证的过程:
1.获取当前的subject。调用securityutils.getsubject();
2.测试当前用户是否已经被认证,即是否已经登录,调用subject的isauthenticated();
3.若没有被认证,则把用户名和密码封装为usernamepasswordtoken对象
4.执行登录:调用subject的lonin(authenticationtoken)方法
5.自定义realm的方法,从数据库中获取对应的记录,返回给shiro
6.由shiro完成对密码的比对
密码的比对是通过authenticatingRealm的credentialsMatcher(凭证匹配器)的属性来进行密码的比对
1.如何把一个字符串加密为MD5
2.替换当前Realm的credentialsMatcher(凭证匹配器)属性.直接使用HashedCredentialsMatcher对象,并设置加密算法即可。
实现:
1.在doGetAuthenticationInfo 方法返回值创建SimpleAuthenticationInfo对象的时候, 需要使用
SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);构造器
2.使用ByteSource.Util.bytes()来计算盐值。盐值需要唯一:一般使用随机字符串或user id
3.使用SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);计算盐值的值
1.log.jsp
<form action="shiro/login" method="POST">
username:<input type="text" name="username">
<br>
password:<input type="text" name="password">
<br>
<input type="submit" value="submit">
</form>
2.list.jsp
<a href="shiro/logout">logout</a>
授权:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="/login.jsp"></property>
<property name="successUrl" value="/list.jsp"></property>
<property name="unauthorizedUrl" value="/unauthorized.jsp"></property>
<!--配置哪些页面需要受保护以及访问这些页面需要的权限
/**应该放在最后
格式:url=拦截器[参数]
anon 可以被匿名访问(不用登陆)
authc 必须认证(即登录)才可以访问
logout 登出
roles 角色过滤器
-->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/shiro/login = anon
/shiro/logout = logout
/list.jsp = roles[user]
/admin.jsp = roles[admin]
/** = authc
</value>
</property>
</bean>
2.授权需要继承AuthorizingRealm类,并实现dogetAuthorizationInfo方法,这个方法重写了认证和授权:
public class testReaml extends AuthorizingRealm {
//用于授权的方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//用于认证的方法
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
3.完整
public class ShiroReam extends AuthorizingRealm {
//认证的方法
//token是currentUser.login(token)传过来的
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.把AuthenticationToken转化为UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.从UsernamePasswordToken中获取username
String username = upToken.getUsername();
//3.调用数据库,从数据库中查询username对应的用户记录
//根据用户名查找用户信息,调用dao
System.out.println("从数据库中获取username:" + username + "所对应的信息");
//4.若用户不存在则可以抛出异常,认证异常子类unknownAccountException异常
if("unknown".equals(username )){
throw new UnknownAccountException("用户不存在");
}
//5.根据用户信息的情况,决定是否需要抛出其他异常AuthenticationExceptin异常(锁定异常)
if("moster".equals(username)){
throw new LockedAccountException("用户被锁定");
}
//6.根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类: SimpleAuthenticationInfo
//一下信息是从数据库中获取的
//1.principal:认证的实体信息,可以是username,也可以是数据表对应的用户实体类对象
Object principal = username;
//2.credentials:从数据表中获取的密码
Object credentials = null;
if ("admin".equals(username)){
credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
}else if("user".equals(username)){
credentials = "098d2c478e9c11555ce2823231e02ec1";
}
//3.realmName:当前realm对象的name,调用父类getName()即可
String realmName = getName();
//4.盐值,因为用户名是唯一的所以拿用户名作为盐的原始值
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
/* SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials ,realmName);*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
return info;
}
//计算盐值
public static void main(String[] args) {
String hashAlgorithmName = "MD5";
Object credentials = "123456";
Object salt = ByteSource.Util.bytes("admin");
int hashIterations = 1024;
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
System.out.println(result);
}
//授权的方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1.从PrincipalCollection中获取登录用户的信息
Object principal = principalCollection.getPrimaryPrincipal();
//2.利用登录的用户的信息来获取当前用户的角色或权限(可能需要查询数据库)
Set<String> roles = new HashSet<String>();
roles.add("user");
if ("admin".equals(principal)){
roles.add("admin");
}
//3.创建 SimpleAuthorizationInfo,并设置其reles属性
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//4.返回SimpleAuthenticationInfo对象。
return info;
}
}