shiro使用场景:
- 登录的验证
- 对指定角色以及权限的验证
- 对URL的验证
shiro使用步骤:
以下针对maven项目管理来说的
1、在pom.xml中添加依赖
<!--shiro start-->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<!-- <dependency> -->
<!-- <groupId>org.slf4j</groupId> -->
<!-- <artifactId>slf4j-log4j12</artifactId> -->
<!-- </dependency> -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!--shiro end-->
2、创建自定义类MyRealm类,继承至Shiro
的AuthorizingRealm
类,用于处理自己的验证逻辑
package com.bsk.shiro;
import java.util.Set;
import javax.annotation.Resource;
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 com.bsk.entity.T_user;
import com.bsk.service.T_UserService;
/**
* Shiro 自定义域
* @author Lenovo
*
*/
public class MyRealm extends AuthorizingRealm{
@Resource
private T_UserService tUserService;
/**
* 用于权限的认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> rolename = tUserService.findRoles(username);
Set<String> permissions = tUserService.findPermissions(username);
info.setRoles(rolename);
info.setStringPermissions(permissions);
return info;
}
/**
* 首先执行这个登录验证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取用户账号
String username = token.getPrincipal().toString();
T_user user = tUserService.findUserByUsername(username);
if(user != null) {
// 将查询到的用户账号和密码存放到authenticationInfo ,用于后面的权限判断。第三个参数随便放一个就行
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()
方法进行验证。
其中,对应上面提到的三个需求,会需要以下三个方法:
findUserByUserName(String username)
根据username查询用户,之后Shiro会根据查询出来的User的密码来和提交上来的密码进行比对。findRoles(String username)
根据username查询该用户的所有角色,用于角色验证。findPermissions(String username)
根据username查询他所拥有的权限信息,用于权限判断。
mapper代码,就是对应的xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.bsk.mapper.T_userMapper" >
<resultMap id="BaseResultMap" type="com.bsk.entity.T_user" >
<result property="id" column="id"/>
<result property="userName" column="userName"/>
<result property="password" column="password"/>
<result property="roleId" column="roleId"/>
</resultMap>
<sql id="Base_Column_List" >
id, username, password,roleId
</sql>
<select id="findUserByUsername" parameterType="String" resultMap="BaseResultMap">
select <include refid="Base_Column_List"/>
from t_user where userName=#{userName}
</select>
<select id="findRoles" parameterType="String" resultType="String">
select r.roleName from t_user u,t_role r where u.roleId=r.id and u.userName=#{userName}
</select>
<select id="findPermissions" parameterType="String" resultType="String">
select p.permissionName from t_user u,t_role r,t_permission p
where u.roleId=r.id and p.roleId=r.id and u.userName=#{userName}
</select>
</mapper>
相对应的controller代码:
package com.bsk.controller;
import javax.annotation.Resource;
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.web.bind.annotation.RequestMapping;
/**
* 后台 Controller
* @author Lenovo
*
*/
import org.springframework.web.servlet.ModelAndView;
import com.bsk.entity.T_user;
import com.bsk.service.T_UserService;
@Controller
@RequestMapping("/")
public class T_UserController {
@Resource
private T_UserService tUserService;
@RequestMapping("/loginAdmin")
public ModelAndView login(T_user user) {
ModelAndView mv = new ModelAndView();
mv.setViewName("admin");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassword());
try {
subject.login(token);
return mv;
} catch (Exception e) {
// 这里将异常打印关闭是因为如果登录失败的话会自动抛异常
// e.printStackTrace();
mv.addObject("error", "用户名或密码错误");
mv.setViewName("login");
return mv;
}
}
@RequestMapping("/admin")
public ModelAndView admin() {
ModelAndView mv = new ModelAndView();
mv.setViewName("admin");
return mv;
}
@RequestMapping("/student")
public ModelAndView student() {
ModelAndView mv = new ModelAndView();
mv.setViewName("admin");
return mv;
}
@RequestMapping("/teacher")
public ModelAndView teacher() {
ModelAndView mv = new ModelAndView();
mv.setViewName("admin");
return mv;
}
}
主要就是login()
方法。逻辑比较简单,只是登录验证的时候不是像之前那样直接查询数据库然后返回是否有用户了,而是调用subject
的login()
方法,就是我上面提到的,调用login()
方法时Shiro
会自动调用我们自定义的MyRealm
类中的doGetAuthenticationInfo()
方法进行验证的,验证逻辑是先根据用户名查询用户,如果查询到的话再将查询到的用户名和密码放到SimpleAuthenticationInfo
对象中,Shiro会自动根据用户输入的密码和查询到的密码进行匹配,如果匹配不上就会抛出异常,匹配上之后就会执行doGetAuthorizationInfo()
进行相应的权限验证。doGetAuthorizationInfo()
方法的处理逻辑也比较简单,根据用户名获取到他所拥有的角色以及权限,然后赋值到SimpleAuthorizationInfo
对象中即可,Shiro就会按照我们配置的XX角色对应XX权限来进行判断,这个配置在下面的整合中会讲到。
3、整合到Spring中
在Spring SpringMVC Mybatis
的基础上进行整合的,这个是大家都比较关心的一步:Spring整合Shiro。
web.xml配置
首先我们需要在web.xml
进行配置Shiro的过滤器。
<!-- shiroFilter start -->
<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>
<!-- shiroFilter end -->
配置还是比较简单的,这样会过滤所有的请求。
之后我们还需要在Spring中配置一个shiroFilter
的bean。
applicationContext.xml配置
<!-- shiro start -->
<!-- 配置自定义Realm -->
<bean id="myRealm" class="com.bsk.shiro.MyRealm" />
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"></property>
</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>
<!-- shiro end -->
在这里我们配置了上文中所提到的自定义myRealm
,这样Shiro就可以按照我们自定义的逻辑来进行权限验证了。
其中,
- /loginAdmin=anon的意思是,发起/loginAdmin这个请求是不需要进行身份认证的,这个请求在这次项目中是一个登录请求,一般对于这样的请求都是不需要身份认证的。
- /admin*=authc表示 /admin,/admin1,/admin2这样的请求都是需要进行身份认证的,不然是不能访问的。
- /student=roles[teacher]表示访问/student请求的用户必须是
teacher
角色,不然是不能进行访问的。 - /teacher=perms[“user:create”]表示访问/teacher请求是需要当前用户具有
user:create
权限才能进行访问的。
4、shiro在前端页面的使用
使用shiro标签库
Shiro还有着强大标签库,可以在前端帮我获取信息和做判断。
登录完成之后显示的界面代码:
<%@ 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>
<shiro:hasRole name="teacher">
这是teacher角色登录:<shiro:principal></shiro:principal>
</shiro:hasRole>
<shiro:hasPermission name="student:create">
有student:create权限信息
</shiro:hasPermission>
<br>
登录成功
</body>
</html>
要想使用Shiro标签,只需要引入一下标签即可:<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
其实英语稍微好点的童鞋应该都能看懂。下面我大概介绍下一些标签的用法:
- 具有
admin
角色才会显示标签内的信息。 - 获取用户信息。默认调用
Subject.getPrincipal()
获取,即 Primary Principal。 - 用户拥有
user:create
这个权限才回显示标签内的信息。
5、可以使用shiro封装的MD5加密,对用户注册的密码进行加密
package com.bsk.shiro;
import org.apache.shiro.crypto.hash.Md5Hash;
/**
* 基于shiro的MD5加密
* @author Lenovo
*
*/
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","bsk") ;
System.out.println(md5);
}
}
代码非常简单,只需要调用Md5Hash(str,salt)
方法即可,这里多了一个参数,第一个参数不用多解释,是需要加密的字符串。第二个参数salt
中文翻译叫盐,加密的时候我们传一个字符串进去,只要这个salt不被泄露出去,那原则上加密之后是无法被解密的,在存用户密码的时候可以使用,感觉还是非常屌的。