shiro配置文件shiro.xml,主要是暴露几个自定义的类
1、跟当前用户有关系的类,MyRealm.java,登陆验证,当前用户具有的角色权限
2、跟项目所有权限有关的类,ChainDefinitionSectionMetaSource.java,加载所有的权限,启动项目的时候加载
3、自定义验证功能的类,MyPermissionAuthorizationFilter.java,这个是权限自定义验证的类,比如一条路径有多个权限,自定义这些权限是或的关系还是并的关系。比如一个因为权限不足而被阻挡的访问,判断是普通浏览器请求还是ajax请求,根据不同请求给出不同的响应。
<?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">
<context:component-scan base-package="shiro"/>
<bean id="myRealm" class="shiro.MyRealm"/>
<!-- 配置SecurityManager的管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 配置需要使用的Realms -->
<property name="realm" ref="myRealm"/>
</bean>
<!-- 配置shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 配置一个安全管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 出现错误之后的跳转路径配置 -->
<property name="loginUrl" value="/user/login"/>
<!-- 认证失败之后的跳转路径页面 -->
<property name="unauthorizedUrl" value="/user/message"/>
<!-- 自定义验证规则 -->
<property name="filters">
<map>
<entry key="perms">
<bean
class="shiro.MyPermissionAuthorizationFilter" />
</entry>
<!--退出过滤器-->
<entry key="logout" value-ref="logoutFilter" />
</map>
</property>
<!-- 自定义一个类,动态为路径添加角色权限等信息 -->
<property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" />
</bean>
<!--自定义LogoutFilter,退出-->
<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="/user/login"/>
</bean>
<bean id="chainDefinitionSectionMetaSource" class="shiro.ChainDefinitionSectionMetaSource">
<property name="filterChainDefinitions">
<value>
/manage/enduser/query=authc,perms[perm1]
/=authc
/manage/enduser/logout=logout
</value>
</property>
</bean>
</beans>
记得将shiro.xml路径加到中,filter要使用shiroFilter,都是修改web.xml
<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_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
classpath:shiro.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- shiroFilter : DelegatingFilterProxy作用是自动到spring容器查找名字为shiroFilter(filter-name)的bean并把所有Filter的操作委托给它。然后将shiroFilter配置到spring容器即可 -->
<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>
</web-app>
自定义的几个类
MyRealm.java
package shiro;
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 org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
/**
* @ClassName MyRealm
* @Description TODO
* @Author shuai
* @Date 2018/6/11 17:07
* @Version 1.0
**/
@Component
public class MyRealm extends AuthorizingRealm {
public MyRealm() {
System.out.println("MyRealm扫描到了!!!");
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取传过来的用户名
String username = (String) token.getPrincipal();
//获取传过来的密码
String password = new String((char[]) token.getCredentials());
//判断该用户是否存在...
return new SimpleAuthenticationInfo(username, password, "testRealm");
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//此时说明用户已经登录成功了(上面方法验证成功后才会进入此方法),此方法用来加载用户的角色和权限
//用来携带角色,权限
SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
//存角色
Set<String> roles = new HashSet<>();
//存权限
Set<String> stringPermissions = new HashSet<>();
//获取当前登录用户的用户名
String username = (String) principalCollection.getPrimaryPrincipal();
//获取用户...
System.out.println("当前用户具有的权限");
stringPermissions.add("hello");
//目前只设置权限,暂时不管角色
//auth.setRoles(roles);
auth.setStringPermissions(stringPermissions);
return auth;
}
}
ChainDefinitionSectionMetaSource.java
package shiro;
import org.apache.shiro.config.Ini;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
/**
* @ClassName ChainDefinitionSectionMetaSource
* @Description TODO
* @Author shuai
* @Date 2018/6/11 17:07
* @Version 1.0
**/
@Component
public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section> {
private String filterChainDefinitions;
/**
* 设置权限格式,与配置文件中自定义的key一致
*/
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
public ChainDefinitionSectionMetaSource() {
System.out.println("ChainDefinitionSectionMetaSource扫描到了!!!");
}
public ChainDefinitionSectionMetaSource(String filterChainDefinitions) {
this.filterChainDefinitions = filterChainDefinitions;
}
@Override
public Ini.Section getObject() {
Ini ini = new Ini();
//加载默认的url
ini.load(filterChainDefinitions);
Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
//加载所有权限...
//循环Resource的url,逐个添加到section中。section就是filterChainDefinitionMap
//里面的键就是链接URL,值就是存在什么条件才能访问该链接
putDefinitionSection(section, "/hello", "hello");
putDefinitionSection(section, "/hello1", "hello1");
return section;
}
private void putDefinitionSection(Ini.Section section, String key, String value) {
System.out.println("加载数据库权限:【key=" + key + "\tvalue=" + value + "】");
section.put(key, MessageFormat.format(PREMISSION_STRING, value));
}
public void setFilterChainDefinitions(String filterChainDefinitions) {
this.filterChainDefinitions = filterChainDefinitions;
}
@Override
public Class<?> getObjectType() {
return this.getClass();
}
@Override
public boolean isSingleton() {
return false;
}
}
MyPermissionAuthorizationFilter.java
package shiro;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
/**
* @ClassName MyPermissionAuthorizationFilter
* @Description TODO
* @Author shuai
* @Date 2018/6/11 17:07
* @Version 1.0
**/
@Component
public class MyPermissionAuthorizationFilter extends AuthorizationFilter {
private static Logger log = LoggerFactory.getLogger(MyPermissionAuthorizationFilter.class);
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
final String AJAX_SIGN = "XMLHttpRequest";
//验证是否为ajax请求时用到
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//ajax请求返回值时用到
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//值为XMLHttpRequest表示ajax请求
String requestType = httpServletRequest.getHeader("X-Requested-With");
Subject subject = getSubject(request, response);
//判断是否登录成功
//未登录
if (subject.getPrincipal() == null) {
if (AJAX_SIGN.equals(requestType)) {
log.info("未登录ajax请求");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.getWriter().write("请登录");
} else {
//此时requestType为null,非ajax请求
log.info("未登录非ajax请求(浏览器)");
//跳转到配置的loginUrl页面
saveRequestAndRedirectToLogin(request, response);
}
} else {
if (AJAX_SIGN.equals(requestType)) {
log.info("已登录ajax请求");
httpServletResponse.setCharacterEncoding("UTF-8");
//setContentType("application/json");
httpServletResponse.getWriter().write("您没有足够权限");
} else {
//此时requestType为null,非ajax请求
log.info("已登录非ajax请求(浏览器)");
//获取配置的unauthorizedUrl页面路径
String unauthorizedUrl = getUnauthorizedUrl();
//存在内容,跳转到配置的unauthorizedUrl页面
if (StringUtils.hasText(unauthorizedUrl)) {
WebUtils.issueRedirect(request, response, unauthorizedUrl);
} else {
log.info("请求拒绝401");
WebUtils.toHttp(response).sendError(401);
}
}
}
return false;
}
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
//获取该请求路径需要的权限集合,即配置在perms[]中括号内的内容(写在shiro.xml配置文件中或者自定义类中)
String[] permsArray = (String[]) mappedValue;
//未配置权限,直接返回
if (permsArray == null || permsArray.length == 0) {
return true;
}
//将该请求路径保存为set集合
Set<String> perms = CollectionUtils.asSet(permsArray);
//获取当前的subject
Subject subject = getSubject(request, response);
//设置验证规则,此时规则为:登录用户具有一条请求路径的权限即可,默认为登录用户需要具备请求路径的所有权限
for (String perm : perms) {
if (subject.isPermitted(perm)) {
return true;
}
}
return false;
}
}
大概就是这个样子,在controller写个登陆验证就好了
package controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName UserController
* @Description TODO
* @Author shuai
* @Date 2018/8/6 17:51
* @Version 1.0
**/
@Controller
@RequestMapping("user")
public class UserController {
@ResponseBody
@RequestMapping(value="login", produces = {"application/json;charset=UTF-8"})
public Object login(){
return "请登录";
}
@ResponseBody
@RequestMapping("validate")
public Object validate(HttpServletRequest request, String username, String password) {
Subject subject = SecurityUtils.getSubject();
//UsernamePasswordToken token = new UsernamePasswordToken(username, password);
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123456");
int code;
String message;
String requestUrl = null;
try {
subject.login(token);
//如果是因为访问其他路径儿跳转到登录页面,savedRequest将有值即想要访问的页面
SavedRequest savedRequest = WebUtils.getSavedRequest(request);
if (savedRequest != null) {
//获取之前访问的路径
requestUrl = savedRequest.getRequestUrl();
} else {
//获取根路径,用来获取首页地址
requestUrl = request.getContextPath();
}
} catch (Exception e) {
}
return null;
}
@RequestMapping("message")
public String message() {
return "message";
}
}
还有很多细致的东西,自定义异常类啊,跳转到原来的访问路径啊,记住登陆啊之类的,慢慢学习吧
shiro需要引用的包pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring-version>4.3.13.RELEASE</spring-version>
<shiro-version>1.3.2</shiro-version>
<freemarker-version>2.3.23</freemarker-version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--spring相关包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- shiro相关包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro-version}</version>
</dependency>
<!-- freemarker包 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker-version}</version>
</dependency>
</dependencies>