Shiro安全框架简介
什么是Shiro?
- Apache的强大灵活的开源框架
- 认证、授权、企业会话管理、安全加密
Shiro与Spring Security比较
Apache Shiro
- 简单、灵活
- 可脱离Spring
- 粒度较粗
Spring Security
- 复杂、笨重
- 不可脱离Spring
- 粒度更细
Shiro整体架构

主要分为两大块,上面Subject是主体部分,下面Security Manager是Shiro的核心。
Subject:
SecurityManager:
Authenticator:
Authentication Strategy:
Authorizer:
SessionManager:
SessionDAO:
CacheManager:
Cryptography:
Realms:
Shiro认证
- 创建SecurityManager
- 主体提交认证
- SecurityManager认证
- Authenticator认证
- Realm验证
代码示例
- 添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
- 编写测试类
package com.ltx.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
public class AuthenticatorTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser() {
simpleAccountRealm.addAccount("Ltx", "123");
}
@Test
public void testAuthenticator() {
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("Ltx", "123");
subject.login(token);
System.out.println("isAuthenticated:"+subject.isAuthenticated());
// 退出
subject.logout();
System.out.println("isAuthenticated:"+subject.isAuthenticated());
}
}
Shiro 授权
- 创建SecurityManager
- 主体授权
- SecurityManager授权
- Authorizer授权
- Realm获取角色权限数据
代码示例
package com.ltx.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
/**
* Shiro授权
*/
public class AuthorizerTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser() {
simpleAccountRealm.addAccount("Ltx", "123", "admin", "user");
}
@Test
public void testAuthorizer() {
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("Ltx", "123");
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
subject.checkRoles("admin", "user");
}
}
Shiro自定义Realm
内置Realm:
- IniRealm
- JdbcRealm
IniRealm 代码示例
package com.ltx.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
/**
* Shiro认证
*/
public class IniRealmTest {
@Test
public void testAuthenticator() {
IniRealm iniRealm = new IniRealm("classpath:user.ini");
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("Ltx", "123");
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
subject.checkRole("admin");
subject.checkPermission("user:update");
}
}
JdbcRealm 代码示例
- 添加依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
- 编写测试类
package com.ltx.test;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
/**
* Shiro认证
*/
public class JdbcRealmTest {
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("666666");
}
@Test
public void testAuthenticator() {
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
jdbcRealm.setPermissionsLookupEnabled(true);
String sql = "select password from test_user where mobile=?";
jdbcRealm.setAuthenticationQuery(sql);
String roleSql = "select role_name from test_user_role where mobile=?";
jdbcRealm.setUserRolesQuery(roleSql);
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("13333333333", "123");
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
/*subject.checkRole("admin");
subject.checkRoles("admin", "user");
subject.checkPermission("user:select");*/
subject.checkRole("user");
}
}
自定义 Realm 代码示例
- 添加Ralm类
package com.ltx.shiro.realm;
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 java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
Map<String, String> userMap = new HashMap<String, String>();
{
userMap.put("Ltx", "123");
super.setName("customRealm");
}
/**
* 用户授权
* @param principals
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) principals.getPrimaryPrincipal();
// 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByUserName(userName);
Set<String> permissions = getPermissionsByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
/**
* 模拟数据库查询用户权限
* @param userName
* @return
*/
private Set<String> getPermissionsByUserName(String userName) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
/**
* 模拟数据库查询用户角色
* @param userName
* @return
*/
private Set<String> getRolesByUserName(String userName) {
Set<String> sets = new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}
/**
* 用户认证
* @param token
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 从主体传过来的认证信息中,获得用户名
String userName = (String) token.getPrincipal();
// 2. 通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if (password==null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName,password,"customRealm");
return authenticationInfo;
}
/**
* 模拟数据库查询凭证
* @param userName
* @return
*/
private String getPasswordByUserName(String userName) {
return userMap.get(userName);
}
}
- 编写测试类
package com.ltx.test;
import com.ltx.shiro.realm.CustomRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class CustomRealmTest {
@Test
public void testAuthenticator() {
CustomRealm customRealm = new CustomRealm();
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("Ltx", "123");
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
/*subject.checkRole("admin");
subject.checkPermission("user:update");*/
subject.checkRole("admin");
subject.checkPermissions("user:add","user:delete");
}
}
Shiro 加密
Shiro 散列配置
- HashedCredentialsMatcher
- 自定义Realm中使用散列
- 盐的使用
代码示例
CustomRealm类
package com.ltx.shiro.realm;
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.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
Map<String, String> userMap = new HashMap<String, String>();
{
userMap.put("Ltx", "516676a5135454394797e58aa76289f7");
super.setName("customRealm");
}
/**
* 用户授权
*
* @param principals
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) principals.getPrimaryPrincipal();
// 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByUserName(userName);
Set<String> permissions = getPermissionsByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
/**
* 模拟数据库查询用户权限
*
* @param userName
* @return
*/
private Set<String> getPermissionsByUserName(String userName) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
/**
* 模拟数据库查询用户角色
*
* @param userName
* @return
*/
private Set<String> getRolesByUserName(String userName) {
Set<String> sets = new HashSet<String>();
sets.add("admin");
sets.add("user");
return sets;
}
/**
* 用户认证
*
* @param token
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 从主体传过来的认证信息中,获得用户名
String userName = (String) token.getPrincipal();
// 2. 通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, "customRealm");
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("Ltx"));
return authenticationInfo;
}
/**
* 模拟数据库查询凭证
*
* @param userName
* @return
*/
private String getPasswordByUserName(String userName) {
return userMap.get(userName);
}
public static void main(String[] args) {
Md5Hash md5Hash = new Md5Hash("123", "Ltx");
System.out.println(md5Hash.toString());
}
}
CustomRealmTest类
package com.ltx.test;
import com.ltx.shiro.realm.CustomRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class CustomRealmTest {
@Test
public void testAuthenticator() {
CustomRealm customRealm = new CustomRealm();
// 1. 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customRealm);
// 2. 主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");
matcher.setHashIterations(1);
customRealm.setCredentialsMatcher(matcher);
// 3. 登录
UsernamePasswordToken token = new UsernamePasswordToken("Ltx", "123");
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
/*subject.checkRole("admin");
subject.checkPermission("user:update");*/
/*subject.checkRole("admin");
subject.checkPermissions("user:add","user:delete");*/
}
}
Shiro集成Spring
- 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
- 配置 web,xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<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>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
- 配置 spring.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.xsd">
<import resource="spring-dao.xml"/>
<context:component-scan base-package="com.ltx"/>
<!--配置Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html=anon
/subLogin=anon
/*=authc
</value>
</property>
</bean>
<!--创建SecurityManager对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
</bean>
<!--配置自定义Realm-->
<bean class="com.ltx.shiro.realm.CustomRealm" id="customRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--配置认证加密-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>
</beans>
- 配置 spring-dao.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="666666"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
- 配置 springmvc.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<context:component-scan base-package="com.ltx.controller"/>
<mvc:annotation-driven/>
<mvc:resources mapping="/*" location="/"/>
<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
- 编写 CustomRealm类
package com.ltx.shiro.realm;
import com.ltx.dao.UserDao;
import com.ltx.vo.User;
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.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import javax.annotation.Resource;
import java.util.*;
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
@Resource
private UserDao userDao;
/**
* 用户授权
*
* @param principals
* @return
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) principals.getPrimaryPrincipal();
// 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByUserName(userName);
Set<String> permissions = getPermissionsByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
/**
* 模拟数据库查询用户权限
*
* @param userName
* @return
*/
private Set<String> getPermissionsByUserName(String userName) {
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
/**
* 模拟数据库查询用户角色
*
* @param userName
* @return
*/
private Set<String> getRolesByUserName(String userName) {
List<String> list = userDao.queryRoleByUserName(userName);
Set<String> sets = new HashSet<String>(list);
return sets;
}
/**
* 用户认证
*
* @param token
* @return
* @throws AuthenticationException
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 从主体传过来的认证信息中,获得用户名
String userName = (String) token.getPrincipal();
// 2. 通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if (password == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, password, "customRealm");
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(userName));
return authenticationInfo;
}
/**
* 模拟数据库查询凭证
*
* @param userName
* @return
*/
private String getPasswordByUserName(String userName) {
User user = userDao.getUserByMobile(userName);
if (user != null) {
return user.getPassword();
}
return null;
}
public static void main(String[] args) {
Md5Hash md5Hash = new Md5Hash("123", "Ltx");
System.out.println(md5Hash.toString());
}
}
- 编写 UserDao类
package com.ltx.dao;
import com.ltx.vo.User;
import java.util.List;
public interface UserDao {
User getUserByMobile(String userName);
List<String> queryRoleByUserName(String userName);
}
- 编写 UserDaoImpl类
package com.ltx.dao.impl;
import com.ltx.dao.UserDao;
import com.ltx.vo.User;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
@Repository
public class UserDaoImpl implements UserDao {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public User getUserByMobile(String userName) {
String sql = "select username,password from wherw username=?";
List<User> list = jdbcTemplate.query(sql, new String[]{userName}, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
});
if (CollectionUtils.isEmpty(list)) {
return null;
}
return list.get(0);
}
@Override
public List<String> queryRoleByUserName(String userName) {
String sql = "select role _name form user_roles where username=?";
return jdbcTemplate.query(sql, new String[]{userName}, new RowMapper<String>() {
@Override
public String mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("role_name");
}
});
}
}
- 编写 UserController类
package com.ltx.controller;
import com.ltx.vo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping(value = "/subLogin", method = RequestMethod.POST,produces = "application/json;charset=utf-8")
@ResponseBody
public String subLogin(User user) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
return e.getMessage();
}
if (subject.hasRole("admin")) {
return "有admin权限";
}
return "无admin权限";
}
@RequiresRoles("admin")
@RequestMapping(value = "/testRole",method = RequestMethod.GET)
@ResponseBody
public String testRole(){
return "testRole success";
}
@RequiresRoles("admin1")
@RequestMapping(value = "/testRole",method = RequestMethod.GET)
@ResponseBody
public String testRole1(){
return "testRole1 success";
}
}
- 编写 index.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>你好我是,凌天香</h2>
</body>
</html>
- 编写 login.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="subLogin" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
Shrio 过滤器
Shrio 内置过滤器
-
认证相关
anon,authBaic,authc,user,logout -
授权相关
perms,roles,ssl,port
自定义过滤器代码示例
- 添加RolesOrFilter类并继承AuthorizationFilter,o参数代表rolels["1","2"]方括号中的内容(可以是角色,也可以权限)
package com.ltx.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
public class RolesOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest, servletResponse);
String[] roles = (String[]) o;
if (roles==null || roles.length==0) {
return true;
}
for (String role : roles) {
if (subject.hasRole(role)) {
return true;
}
}
return false;
}
}
- 自定义过滤器添加到 filters 中
<?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:util="http://www.springframework.org/schema/util"
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">
<import resource="spring-dao.xml"/>
<context:component-scan base-package="com.ltx"/>
<!--配置Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html=anon
/subLogin=anon
<!--/testRole=roles[admin]
/testRole1=roles["admin","admin1"]
/testPerms=perms["user:delete"]
/testPerms1=perms["user:delete","user:update"]-->
/testrole=roles["admin","admin1"]
/testRole1=rolesOr["admin","admin1"]
/*=authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr " value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>
<bean class="com.ltx.filter.RolesOrFilter" id="rolesOrFilter"/>
<!--创建SecurityManager对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
</bean>
<!--配置自定义Realm-->
<bean class="com.ltx.shiro.realm.CustomRealm" id="customRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--配置认证加密-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>
</beans>
Shiro 会话管理
Shiro Session 管理
- SessionManager、SessionDao
- Redis实现Session共享
- Redis实现Session共享存在的问题(会多次读取jedis)
代码示例
- 编写RedisSessionDao类
package com.ltx.session;
import com.ltx.util.JedisUtil;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.util.CollectionUtils;
import org.springframework.util.SerializationUtils;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class RedisSessionDao extends AbstractSessionDAO {
@Resource
private JedisUtil jedisUtil;
private final String SHIRO_SESSION_PREFIX = "ltx-session:";
/**
* 获取key
* @param key
* @return
*/
private byte[] getKey(String key) {
return (SHIRO_SESSION_PREFIX + key).getBytes();
}
/**
* 保存session
* @param session
*/
private void saveSession(Session session){
if (session!=null&&session.getId()!=null) {
byte[] key = getKey(session.getId().toString());
byte[] value = SerializationUtils.serialize(session);
jedisUtil.set(key, value);
jedisUtil.expire(key, 600);
}
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
assignSessionId(session,sessionId);
saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
System.out.println("read session");
if (sessionId == null) {
return null;
}
byte[] key = getKey(sessionId.toString());
byte[] value = jedisUtil.get(key);
return (Session) SerializationUtils.deserialize(value);
}
@Override
public void update(Session session) throws UnknownSessionException {
saveSession(session);
}
@Override
public void delete(Session session) {
if (session==null || session.getId()==null) {
return;
}
byte[] key = getKey(session.getId().toString());
jedisUtil.del(key);
}
@Override
public Collection<Session> getActiveSessions() {
Set<byte[]> keys = jedisUtil.keys(SHIRO_SESSION_PREFIX);
Set<Session> sessions = new HashSet<>();
if (CollectionUtils.isEmpty(keys)) {
return sessions;
}
for (byte[] key : keys) {
Session session = (Session) SerializationUtils.deserialize(jedisUtil.get(key));
sessions.add(session);
}
return sessions;
}
}
- 编写CustomSessionManager类
package com.ltx.session;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import javax.servlet.ServletRequest;
import java.io.Serializable;
/**
* 自定义session管理器(减轻redis压力将session放入reques中,没有取到再从redis中获取)
*/
public class CustomSessionManager extends DefaultWebSessionManager {
@Override
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
Serializable sessionId = getSessionId(sessionKey);
ServletRequest request = null;
if (sessionKey instanceof WebSessionKey) {
request = ((WebSessionKey) sessionKey).getServletRequest();
}
if (request != null && sessionId != null) {
Session session = (Session) request.getAttribute(sessionId.toString());
if (session!=null) {
return session;
}
}
Session session = super.retrieveSession(sessionKey);
if (request!=null&&sessionId!=null) {
request.setAttribute(sessionId.toString(),session);
}
return session;
}
}
- 编写spring.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:util="http://www.springframework.org/schema/util"
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">
<import resource="spring-dao.xml"/>
<import resource="spring-redis.xml"/>
<context:component-scan base-package="com.ltx"/>
<!--配置Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html=anon
/subLogin=anon
<!--/testRole=roles[admin]
/testRole1=roles["admin","admin1"]
/testPerms=perms["user:delete"]
/testPerms1=perms["user:delete","user:update"]-->
/testrole=roles["admin","admin1"]
/testRole1=rolesOr["admin","admin1"]
/*=authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr " value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>
<bean class="com.ltx.filter.RolesOrFilter" id="rolesOrFilter"/>
<!--创建SecurityManager对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!--配置自定义Realm-->
<bean class="com.ltx.shiro.realm.CustomRealm" id="customRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--配置认证加密-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>
<!--创建Session管理器-->
<bean class="com.ltx.session.CustomSessionManager" id="sessionManager">
<property name="sessionDAO" ref="redisSessionDao"/>
</bean>
<bean class="com.ltx.session.RedisSessionDao" id="redisSessionDao"/>
</beans>
- 添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
Shiro缓存管理
- CacheManager、Cache
- Redis实现CacheManager
代码示例
- 编写RedisCacheManger类
package com.ltx.cache;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import javax.annotation.Resource;
/**
* 缓存管理器
*/
public class RedisCacheManger implements CacheManager {
@Resource
private RedisCache redisCache;
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
return redisCache;
}
}
- 编写RedisCache类
package com.ltx.cache;
import com.ltx.util.JedisUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Set;
@Component
public class RedisCache<K, V> implements Cache<K, V> {
@Resource
private JedisUtil jedisUtil;
private final String CACHE_PERFIX = "ltx-cache";
private byte[] getKey(K k) {
if (k instanceof String) {
return (CACHE_PERFIX + k).getBytes();
}
return SerializationUtils.serialize(k);
}
@Override
public V get(K k) throws CacheException {
System.out.println("从redis中获取权限数据");
byte[] value=jedisUtil.get(getKey(k));
if (value!=null) {
return (V) SerializationUtils.deserialize(value);
}
return null;
}
@Override
public V put(K k, V v) throws CacheException {
byte[] key =getKey(k);
byte[] value = SerializationUtils.serialize(v);
jedisUtil.set(key,value);
jedisUtil.expire(key,600);
return v;
}
@Override
public V remove(K k) throws CacheException {
byte[] key = getKey(k);
byte[] value = jedisUtil.get(key);
jedisUtil.del(key);
if (value!=null) {
return (V) SerializationUtils.deserialize(value);
}
return null;
}
@Override
public void clear() throws CacheException {
}
@Override
public int size() {
return 0;
}
@Override
public Set<K> keys() {
return null;
}
@Override
public Collection<V> values() {
return null;
}
}
- 编写spring.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:util="http://www.springframework.org/schema/util"
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">
<import resource="spring-dao.xml"/>
<import resource="spring-redis.xml"/>
<context:component-scan base-package="com.ltx"/>
<!--配置Shiro拦截器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/login.html=anon
/subLogin=anon
<!--/testRole=roles[admin]
/testRole1=roles["admin","admin1"]
/testPerms=perms["user:delete"]
/testPerms1=perms["user:delete","user:update"]-->
/testrole=roles["admin","admin1"]
/testRole1=rolesOr["admin","admin1"]
/*=authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr " value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>
<bean class="com.ltx.filter.RolesOrFilter" id="rolesOrFilter"/>
<!--创建SecurityManager对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManger"/>
</bean>
<!--配置自定义Realm-->
<bean class="com.ltx.shiro.realm.CustomRealm" id="customRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--配置认证加密-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>
<!--创建Session管理器-->
<bean class="com.ltx.session.CustomSessionManager" id="sessionManager">
<property name="sessionDAO" ref="redisSessionDao"/>
</bean>
<bean class="com.ltx.session.RedisSessionDao" id="redisSessionDao"/>
<!--创建缓存管理器-->
<bean class="com.ltx.cache.RedisCacheManger" id="cacheManger"/>
</beans>
Shiro RememberMe
- 添加代码 token.setRememberMe(user.isRememberMe());
package com.ltx.controller;
import com.ltx.vo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping(value = "/subLogin", method = RequestMethod.POST,produces = "application/json;charset=utf-8")
@ResponseBody
public String subLogin(User user) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
token.setRememberMe(user.isRememberMe());
subject.login(token);
} catch (AuthenticationException e) {
return e.getMessage();
}
if (subject.hasRole("admin")) {
return "有admin权限";
}
return "无admin权限";
}
@RequestMapping(value = "/testRole",method = RequestMethod.GET)
@ResponseBody
public String testRole(){
return "testRole success";
}
@RequestMapping(value = "/testRole",method = RequestMethod.GET)
@ResponseBody
public String testRole1(){
return "testRole1 success";
}
@RequestMapping(value = "/testPerms",method = RequestMethod.GET)
@ResponseBody
public String testPerms(){
return "testPerms success";
}
@RequestMapping(value = "/testPerms1",method = RequestMethod.GET)
@ResponseBody
public String testPerms1(){
return "testPerms1 success";
}
}
- 添加代码 <input type="checkbox" name="rememberMe"> 记住我
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="subLogin" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="checkbox" name="rememberMe"> 记住我<br>
<input type="submit" value="登录">
</form>
</body>
</html>
- spring.xml添加代码
<!--创建SecurityManager对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="customRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManger"/>
<property name="rememberMeManager" ref="cookieRememberMeManager"/>
</bean>
<bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="cookieRememberMeManager">
<property name="cookie" ref="cookie"/>
</bean>
<bean class="org.apache.shiro.web.servlet.SimpleCookie" id="cookie">
<constructor-arg value="rememberMe"/>
<property name="maxAge" value="2000000"/>
</bean>
网友评论