1 pom.xml 文件添加依赖
<!-- 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 文件添加shiro过滤器
<!-- shiro过滤器 -->
<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>
<!-- shiro的filter-mapping-->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3 spring-shiro.xml spring整合shiro配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- 导入数据库的相关配置 -->
<!--<import resource="classpath:spring/spring-mybatis.xml"/>-->
<!--自定义过滤器******-->
<bean id="loginFilter" class="com.battery.filter.LoginFilter"></bean>
<bean id="myPermFilter" class="com.battery.filter.PermsFilter"></bean>
<!-- 对应于web.xml中配置的那个shiroFilter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<!--<property name="loginUrl" value="/"/>-->
<!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码) -->
<!-- <property name="successUrl" value="/" ></property> -->
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<!--<property name="unauthorizedUrl" value="/error/unauthorized"/>-->
<property name="filterChainDefinitions">
<value>
<!--首页,登录,登出不需要验证,其他的都需要验证-->
/=anon
/backStage/admin/login.action=anon
<!--/adminUser/logout=anon-->
<!--/adminUser/logout=anon-->
<!-- 需要授权的url -->
<!--/backStage/role/*.action = myPerm["1"]-->
/backStage/admin/*.action = myPerm["13"]
<!--<!–不要使用/**=authc 因为,如果设置了,他就会跳到login.jsp这个登录页面–>-->
<!--<!–/**=loginFilter–>-->
/backStage/**=loginFilter
<!-- "id":"13" -->
<!-- "permissionName": "开放管理员权限 "-->
</value>
</property>
<property name="filters">
<map>
<!--自定义过滤器******-->
<entry key="loginFilter" value-ref="loginFilter"/>
<entry key="myPerm" value-ref="myPermFilter"/>
</map>
</property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- 数据库保存的密码是使用算法加密的,所以这里需要配置一个密码匹配对象 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="3"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- 缓存管理 -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean>
<!--
使用Shiro自带的JdbcRealm类
指定密码匹配所需要用到的加密对象
指定存储用户、角色、权限许可的数据源及相关查询语句
-->
<!--<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">-->
<!--<property name="credentialsMatcher" ref="credentialsMatcher"></property>-->
<!--<property name="permissionsLookupEnabled" value="true"></property>-->
<!--<property name="dataSource" ref="dataSource"></property>-->
<!--<property name="authenticationQuery"-->
<!--value="SELECT password FROM admin_user WHERE userName = ?"></property>-->
<!--<property name="userRolesQuery"-->
<!--value="SELECT roleName from user_role left join role using(id) left join admin_user using(id) WHERE userName = ?"></property>-->
<!--<!–基于用户到权限的–>-->
<!--<property name="permissionsQuery"-->
<!--value="SELECT permissionName FROM user_permission left join admin_user using(id) left join permission using(id) WHERE userName = ?"></property>-->
<!--<!–基于角色-权限的–>-->
<!--<!–<property name="permissionsQuery"–>-->
<!--<!–value="SELECT permissionName FROM role_permission left join role using(id) left join permission using(id) WHERE roleName = ?"></property>–>-->
<!--</bean>-->
<!-- 項目自定义的Realm -->
<bean id="shiroDbRealm" class="com.battery.realm.ShiroDbRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
<!-- 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false -->
<!--<property name="cachingEnabled" value="false"/>-->
<!--<property name="authenticationCachingEnabled" value="false"/>-->
<!-- 缓存AuthenticationInfo信息的缓存名称 -->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!-- 缓存AuthorizationInfo信息的缓存名称 -->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
<property name="authorizationCachingEnabled" value="false"/>
<property name="authenticationCachingEnabled" value="false"/>
</bean>
<!-- Shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm"></property>
<property name="cacheManager" ref="shiroCacheManager"></property>
</bean>
<!-- 开启shiro注解-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
4 ShiroDbRealm.java 自定义Realm类,用于登录信息比对和授权
/**
* 自定义Realm
*
* @author ellen
* @version 1.0
* @date 2018/11/3
*/
public class ShiroDbRealm extends AuthorizingRealm {
@Autowired
IAdminService adminService;
/**
* Shiro权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//这里转化为String是因为传进来的时候也是String类型
String userName = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//这里返回的是权限的id不是权限的名字
List<String> permIds = adminService.getPermissionIdByName(userName);
info.setStringPermissions(new HashSet<String>(permIds));
return info;
}
/**
* Shiro登录认证(原理:用户提交 用户名和密码 --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ---- shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
//从数据库中查找用户
Admin admin = adminService.selectOne(new EntityWrapper<Admin>().eq("admin_name", userName));
//账号不存在
if (admin == null) {
return null;
}
//这里传入正确的用户名跟密码,让shiro来比较
return new SimpleAuthenticationInfo(admin.getAdminName(), admin.getAdminPassword(), getName());
}
/**
* 清除用户认证信息
*
* @param principal
*/
protected void clearCachedAuthenticationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(
principal, getName());
clearCachedAuthenticationInfo(principals);
}
/**
* 清除所有用户授权信息缓存.
*/
public void clearAllCachedAuthorizationInfo() {
Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
if (cache != null) {
for (Object key : cache.keys()) {
cache.remove(key);
}
}
}
}
5 LoginFilter.java 自定义登录过滤器,用于查看是否进行过登录认证
/**
* AccessControlFilter是所有的URL都经过的过滤器
* AuthorizationFilter是需要验证的url才经过的过滤器
*
* @author ellen
* @version 1.0
* @date 2018/11/3
*/
public class loginFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse response, Object o) throws Exception {
//判断用户是否登录,如果登录,返回true ,如果没登录,就返回没有登录
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
return true;
} else {
//这句话的意思,是让浏览器用utf8来解析返回的数据
response.setContentType("text/html;charset=UTF-8");
//这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSONObject.toJSONString(ResponseBean.createNoLogin("您未登录,请先登录!")));
writer.close();
return false;
}
}
}
6 PermsFilter.java 自定义权限过滤器,用于拦截需要授权的请求
/**
* 自定义权限过滤器
*
* @author ellen
* @version 1.0
* @date 2018/11/3
*/
public class PermsFilter extends PermissionsAuthorizationFilter {
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = getSubject(request, response);
if (!subject.isAuthenticated()) {
//这句话的意思,是让浏览器用utf8来解析返回的数据
response.setContentType("text/html;charset=UTF-8");
//这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSONObject.toJSONString(ResponseBean.createNoLogin("您未登录,请先登录!")));
writer.close();
return false;
}
String[] perms = (String[]) mappedValue;
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
if (perms.length == 1) {
if (!subject.isPermitted(perms[0])) {
noPerm(request, response);
isPermitted = false;
}
} else {
if (!subject.isPermittedAll(perms)) {
noPerm(request, response);
isPermitted = false;
}
}
}
return isPermitted;
}
/**
* 无权限返回
*
* @param request
* @param response
* @throws IOException
*/
private void noPerm(ServletRequest request, ServletResponse response) throws IOException {
//这句话的意思,是让浏览器用utf8来解析返回的数据
response.setContentType("text/html;charset=UTF-8");
//这句话的意思,是告诉servlet用UTF-8转码,而不是用默认的ISO8859
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.write(JSONObject.toJSONString(ResponseBean.createNoPower("您无权限操作!")));
writer.close();
}
}
7 login请求方法,参考
/**
* 管理员登录
*
* @param admin
* @param request
* @return
*/
@RequestMapping("/login")
public ResponseBean login(Admin admin, HttpServletRequest request) {
if (StringUtil.isEmpty(admin.getAdminName()) || StringUtil.isEmpty(admin.getAdminPassword())) {
return ResponseBean.createError("用户名或密码不能为空");
}
UsernamePasswordToken token = new UsernamePasswordToken(admin.getAdminName(), admin.getAdminPassword());
token.setRememberMe(false);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
//将用户存进session
Admin currentAdmin = adminService.selectOne(new EntityWrapper<Admin>().eq("admin_name", admin.getAdminName()));
request.getSession().setAttribute(SessionConst.LOGIN_ADMIN_SESSION_KEY, currentAdmin);
return ResponseBean.createSuccess().setMessage("登录成功").addData("admin", admin);
} catch (IncorrectCredentialsException e) {
return ResponseBean.createError("登录密码错误. Password for account " + token.getPrincipal() + " was incorrect.");
} catch (ExcessiveAttemptsException e) {
return ResponseBean.createError("登录失败次数过多");
} catch (LockedAccountException e) {
return ResponseBean.createError("帐号已被锁定. The account for username " + token.getPrincipal() + " was locked.");
} catch (DisabledAccountException e) {
return ResponseBean.createError("帐号已被禁用. The account for username " + token.getPrincipal() + " was disabled.");
} catch (ExpiredCredentialsException e) {
return ResponseBean.createError("帐号已过期. the account for username " + token.getPrincipal() + " was expired.");
} catch (UnknownAccountException e) {
return ResponseBean.createError("帐号不存在. There is no user with username of " + token.getPrincipal());
} catch (UnauthorizedException e) {
return ResponseBean.createError("您没有得到相应的授权!" + e.getMessage());
}
}