shiro核心功能:登录,授权验证
shiro:核心组件:①Subject:与程序交互的对象
②SecurityManager:安全管理器,shiro的核心;所有与安全相关的操作都与SecurityManager交互;且管理着所有的Subject
③Realms:Shiro从Realm获取安全数据,SecurityManager验证用户身份,必须要从Realm获取相应的用户信息
④ShiroFilterFactoryBean:指定路径拦截规则
1.创建ssm框架工程
2.数据库:
3.实体类和方法:
给大家看一下我建立的包路径,防止后面部分代码导包看不明白
4.在pom.xml里导入shiro相关的jar包
<!--Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
5.在工程中创建继承ShiroFilterFactoryBean的类:
package com.xcxy.shiro.factory;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.xcxy.user.dao.MenuMapper;
import com.xcxy.user.entity.Menu;
import com.xcxy.user.entity.Role;
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean{
@Autowired
private MenuMapper mm;
//{0}为占位符 ROLE_STRING是用于字符串拼接
private static final String ROLE_STRING = "roles[{0}]";
//默认权限
public static String filterChainDefinitions="";
/*重写设置过滤规则*/
@Override
public void setFilterChainDefinitions(String definitions) {
System.out.println("$$$$$$$$$$$$$$");
//每次都付给filterChainDefinitions
filterChainDefinitions = definitions;
Ini ini = new Ini();
ini.load(definitions);
//did they explicitly state a 'urls' section? Not necessary, but just in case:
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
//no urls section. Since this _is_ a urls chain definition property, just assume the
//default section contains only the definitions:
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
Map<String,String[]> permsMap = new HashMap<String, String[]>();
/*查询菜单表*/
List<Menu> menus=mm.findMenuAll();
for (Menu menu : menus) {
/*获取每个权限对应的所有角色*/
List<Role> rs=menu.getRoles();
String[] roleNames=new String[rs.size()];
/*获取角色名集合*/
for(int i=0;i<rs.size();i++){
roleNames[i]=rs.get(i).getrName();
}
/*键值对存储,相当于此url哪些角色可以进入*/
permsMap.put(menu.getmUrl(), roleNames);
}
//遍历拿出map中存放的URL和角色名
for (String url : permsMap.keySet()) {
System.out.println("路径:"+url);
//通过路径取得对应的角色
String[] roles = permsMap.get(url);//map中,根据key的内容获取其value
StringBuilder sb = new StringBuilder();
for (String role : roles) {
sb.append(role).append(",");
}
//截取最后一个,如(admin,test,)
String str = sb.substring(0,sb.length()-1);
System.out.println("str:"+str+"%%%%%%%%%%%");
System.out.println(permsMap+"************");
//把对应的路径以及权限放到section中, MessageFormat.format(ROLE_STRING, str) 替换占位符{0}
System.out.println("MessageFormat.format(ROLE_STRING, str):"+MessageFormat.format(ROLE_STRING, str));
section.put(url, MessageFormat.format(ROLE_STRING, str));
}
/*代表所有的页面都需要权限才能访问*/
section.put("/**", "authc");
setFilterChainDefinitionMap(section);
}
}
6.创建自定义类数据源:
package com.xcxy.shiro.realm;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import com.xcxy.user.dao.UserMapper;
import com.xcxy.user.entity.Role;
import com.xcxy.user.entity.User;
public class MyRealm extends AuthorizingRealm{
@Autowired
private UserMapper usermapper;
/**
*
* 授权方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
// 1. 从PrincipalCollection中来获取登陆用户的信息
Object principal = principals.getPrimaryPrincipal();
System.out.println("当前登陆的用户:"+principal);
// 2. 利用登陆的用户信息来获取用户当前的角色以及权限(可能查询数据库)
Set<String> set = new HashSet<String>();
//查询User通过账号
User user = usermapper.findUserByUserName((String)principal);
System.out.println(user+"*********");
//得到User对应的角色
Role role = user.getRole();
//将角色放进集合中
set.add(role.getrName());
System.out.println("set里的值:"+set);
// 3. 创建SimpleAuthorizationInfo,并设置其reles属性
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(set);
// 4. 返回SimpleAuthorizationInfo对象
return simpleAuthorizationInfo;
}
/**
* 认证方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// 从token拿到用户名
String userName = (String) token.getPrincipal();
//通过用户名去数据库查询
System.out.println("============================="+userName);
User user=usermapper.findUserByUserName(userName);
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
//发送用户名-main页面欢迎xx登录
session.setAttribute("username", user.getuName());
// 获取盐,通常用账号
ByteSource credentialsSalt = ByteSource.Util.bytes(userName);
// 盐值加密——密码不易被破解
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName,user.getuPassword(),credentialsSalt,getName());
// SimpleAuthenticationInfo simpleAuthenticationInfo = new
// SimpleAuthenticationInfo(user,
// "64c8b1e43d8ba3115ab40bcea57f010b",getName());
return simpleAuthenticationInfo;
}
}
}
7.创建加密工具类(需要测试类获取加密后的密码存入数据库):
package com.xcxy.shiro.util;
import org.apache.shiro.crypto.hash.SimpleHash;
/**
* 加密工具类
* @author john
*
*/
public class MD5 {
//加密类型
private static String hashName="MD5";
//加密次数
private static int hashNum=1024;
public static Object getMD5(String pwd,String salt){
Object obj=new SimpleHash(hashName, pwd, salt, hashNum);
return obj;
}
}
8.创建自定义过滤器
package com.xcxy.shiro.util;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
public class UserRolesAuthorizationFilter extends AuthorizationFilter{
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;
if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
return true;
}
for (int i = 0; i < rolesArray.length; i++) {
if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
return true;
}
}
return false;
}
}
9.在applicationContext.xml添加shiro相关的配置
<!-- shiro配置 -->
<bean id="myRealm" class="com.xcxy.shiro.realm.MyRealm"> <!-- 配置自定义类数据源 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property> <!-- 加密算法的名称 -->
<property name="hashIterations" value="1024"></property> <!-- 配置加密的次数 -->
</bean>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"></property>
</bean>
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
<!-- 配置核心过滤器 -->
<bean id="shiroFilter" class="com.xcxy.shiro.factory.MyShiroFilterFactoryBean"><!-- 自定义继承ShiroFilterFactoryBean的类 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 除anon权限以外的所有没登陆的访问都到这 -->
<property name="loginUrl" value="/system/gologin" />
<!-- 登陆成功后显示的页面 -->
<property name="successUrl" value="/system/main" />
<!-- 登录后访问没有权限的页面后跳转的页面 -->
<property name="unauthorizedUrl" value="/system/error" />
<property name="filters">
<map>
<entry key="roles" value-ref="roleOrFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/static/**=anon
/system/login=anon <!-- anon表示匿名访问,就是不用登录 -->
/system/gologin=anon
/system/getVerifyCode=anon <!-- 获取验证码 -->
/system/register=anon <!-- 注册 -->
/system/loginout=logout
<!-- anon表示匿名访问,就是不用登录 -->
<!-- /**/*.do = authc --> <!-- authc表示要登录后,才能访问 -->
</value>
</property>
</bean>
<!-- 自定义的过滤器,用来判断当前用户是否是roleOrFilter["comm,test"]中的某个角色 -->
<bean id="roleOrFilter" class="com.xcxy.shiro.util.UserRolesAuthorizationFilter" />
10.在web.xml中配置shiro过滤器:
<!-- 配置shiro的过滤器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
11.在WEB-INFO创建页面:
12.controller层
/**
* 登录验证
* @return
*/
@PostMapping("/login")
public String login(String username,String password,HttpServletRequest request){
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
try {
/*用户验证*/
subject.login(token);
/*登录成功,进入主页面*/
return "system/main";
} catch (AuthenticationException e) {
/*登录失败,返回登录页面*/
e.printStackTrace();
request.getSession().setAttribute("error", "账号密码错误");
return "system/login";
}
}