使用Shiro的时候我们要考虑在什么样的环境下使用:
1.登录的验证
2.对指定角色的验证
3.对URL的验证
基于这三个需求,我们有三个方法:
1. findUserByUserName(String username)
根据username查询用户,之后Shiro会根据查询出来的User的密码来和提交上来的密码进行比对。
2. findRoles(String username)
根据username查询该用户的所有角色,用于角色验证。
3. findPermissions(String username)
根据username查询他所拥有的权限信息,用于权限判断。
创建自定义的MyRealm
类,继承Shiro
的AuthorizingRealm
类,用于处理自己的验证逻辑:
package com.crossoverJie.shiro;
import com.crossoverJie.pojo.T_user;
import com.crossoverJie.service.T_userService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import javax.annotation.Resource;
import java.util.Set;
/**
* Created with IDEA
* Created by ${joy.jie}
* Shiro自定义域
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private T_userService t_userService;
/**
* 用于的权限的认证。
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString() ;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo() ;
Set<String> roleName = t_userService.findRoles(username) ;
Set<String> permissions = t_userService.findPermissions(username) ;
info.setRoles(roleName);
info.setStringPermissions(permissions);
return info;
}
/**
* 首先执行这个登录验证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//获取用户账号
String username = token.getPrincipal().toString() ;
T_user user = t_userService.findUserByUsername(username) ;
if (user != null){
//将查询到的用户账号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入realName。
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),
"a") ;
return authenticationInfo ;
}else{
return null ;
}
}
}
继承
AuthorizingRealm
类之后重写这两个方法,
doGetAuthorizationInfo
,
doGetAuthenticationInfo
doGetAuthenticationInfo
是用于登录验证的,在登录的时候需要将数据封装到Shiro
的一个token
中,执行shiro的login()
方法,之后只要我们将MyRealm
这个类配置到Spring中,登录的时候Shiro
就会自动的调用doGetAuthenticationInfo()
方法进行验证。
登陆controller如下:
package com.crossoverJie.controller;
import com.crossoverJie.pojo.T_user;
import com.crossoverJie.service.T_userService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
/**
* Created with IDEA
* Created by ${joy.jie}
* 后台Controller
*/
@Controller
@RequestMapping("/")
public class T_userController {
@Resource
private T_userService t_userService ;
@RequestMapping("/loginAdmin")
public String login(T_user user, Model model){
Subject subject = SecurityUtils.getSubject() ;
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassword()) ;
try {
subject.login(token);
return "admin" ;
}catch (Exception e){
//这里将异常打印关闭是因为如果登录失败的话会自动抛异常
// e.printStackTrace();
model.addAttribute("error","用户名或密码错误") ;
return "../../login" ;
}
}
@RequestMapping("/admin")
public String admin(){
return "admin";
}
@RequestMapping("/student")
public String student(){
return "admin" ;
}
@RequestMapping("/teacher")
public String teacher(){
return "admin" ;
}
}
调用
login()
方法时
Shiro
会自动调用我们自定义的
MyRealm
类中的
doGetAuthenticationInfo()
方法进行验证的,验证逻辑是先根据用户名查询用户,如果查询到的话再将查询到的用户名和密码放到
SimpleAuthenticationInfo
对象中,Shiro会自动根据用户输入的密码和查询到的密码进行匹配,如果匹配不上就会抛出异常,匹配上之后就会执行
doGetAuthorizationInfo()
进行相应的权限验证。
doGetAuthorizationInfo()
方法的处理逻辑也比较简单,根据用户名获取到他所拥有的角色以及权限,然后赋值到
SimpleAuthorizationInfo
对象中即可,Shiro就会按照我们配置的XX角色对应XX权限来进行判断;
集成spring
web.xml配置
<!-- shiro过滤器定义 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<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>
spring-mybatis.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">
<!-- 自动扫描 -->
<context:component-scan base-package="com.crossoverJie" />
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 指定连接数据库的驱动 -->
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="3" />
<property name="minIdle" value="3" />
<property name="maxActive" value="20" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="20" />
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat" />
</bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.crossoverJie.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置自定义Realm -->
<bean id="myRealm" class="com.crossoverJie.shiro.MyRealm"/>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- Shiro过滤器 核心-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login.jsp"/>
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/nopower.jsp"/>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
<!--anon 表示匿名访问,不需要认证以及授权-->
/loginAdmin=anon
<!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 开启Shiro注解 -->
<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>
- /loginAdmin=anon的意思的意思是,发起/loginAdmin这个请求是不需要进行身份认证的,这个请求在这次项目中是一个登录请求,一般对于这样的请求都是不需要身份认证的。
- /admin*=authc表示 /admin,/admin1,/admin2这样的请求都是需要进行身份认证的,不然是不能访问的。
- /visitor=roles[menber]表示访问/visitor请求的用户必须是
member
角色,不然是不能进行访问的。 - /member=perms[“user:create”]表示访问/member请求是需要当前用户具有
user:create
权限才能进行访问的。
Shiro标签库
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2016/7/14
Time: 13:17
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>后台</title>
</head>
<body>
<shiro:hasRole name="admin">
这是admin角色登录:<shiro:principal></shiro:principal>
</shiro:hasRole>
<shiro:hasPermission name="user:create">
有user:create权限信息
</shiro:hasPermission>
<br>
登录成功
</body>
</html>
MD5加密
Shiro还封装了MD5加密package com.crossoverJie.shiro;
import org.apache.shiro.crypto.hash.Md5Hash;
/**
* Created with IDEA
* 基于Shiro的MD5加密
* Created by ${joy.jie}
*/
public class MD5Util {
public static String md5(String str,String salt){
return new Md5Hash(str,salt).toString() ;
}
public static void main(String[] args) {
String md5 = md5("abc123","crossoverjie") ;
System.out.println(md5);
}
}
第一个参数不用多解释,是需要加密的字符串。第二个参数
salt 加
盐,加密的时候我们传一个字符串进去