Shiro入门到整合redis
用户操作进入Security Manager ,Security Manager 通过Authentication认证器想Reaml获取认证数据,通过Authorizer向Reaml获取权限数据
SimpleAccountRealm realm = new SimpleAccountRealm();
@Before
public void adduser(){
realm.addAccount("mark", "12345","admin");
}
@Test
public void testAuthentication(){
//1.构建SecurtyManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(realm);
//2.构建主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//构建认证请求
UsernamePasswordToken token = new UsernamePasswordToken("mark", "12345");
//进行认证
subject.login(token);
//判断认证是否成功,认证失败会报异常
System.out.println("isAuthentication :"+subject.isAuthenticated());
//判断是否存在角色
subject.checkRole("admin");
}
这儿采用的是SimpleAccountReal,Shiro还有IniReaml和JdbcReam两种内置的Reaml
IniReaml采用的是配置文件的格式
user.ini:
[users] mark=12345,admin [roles] admin=user:delete
JdbcReam使用的数据库的格式
如果没有自定义查询语句,Shiro则会使用默认的查询语句默认的表格式为:



代码示例如下: 构建数据源:
DruidDataSource dateSource = new DruidDataSource();
{
dateSource.setUrl("jdbc:mysql://localhost:3306/test");
dateSource.setPassword("root");
dateSource.setUsername("root");
}
@Test
public void testAuthentication(){
//新建JDBCRealm
JdbcRealm jdbcRealm = new JdbcRealm();
//添加数据源
jdbcRealm.setDataSource(dateSource);
//开启jdbcReaml的权限功能
jdbcRealm.setPermissionsLookupEnabled(true);
//1.构建SecurtyManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
//2.构建主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//构建认证请求
UsernamePasswordToken token = new UsernamePasswordToken("admin", "testadmin");
//进行认证
subject.login(token);
//判断认证是否成功,认证失败会报异常
System.out.println("isAuthentication :"+subject.isAuthenticated());
//判断是否存在角色
subject.checkRole("admin");
//判断是否具有某个权限
subject.checkPermission("user:delete");
}
自定义Realm
1、自定义Realm需要继承AuthorizingRealm这个类
public class CustomRealm extends AuthorizingRealm{
/**
* 授权方法
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取主体传来的已认证信息
String username = (String) principalCollection.getPrimaryPrincipal();
//获取对象的角色
Set<String> roles = getRolesByUsername(username);
//获取对象的权限
Set<String> permissions = getPermissionsByUsername(username);
//构造返回值
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
/**
* 认证方法
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从主体转过来的认证信息中获取用户名
String username = (String) authenticationToken.getPrincipal();
//通过用户名到数据库中获取凭证
String password = getpasswordbyUserName(username);
if(password==null){
return null;
}
//构造返回值
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("cjl",password,"customRealm");
return authenticationInfo;
}
}
用法和上面的一致。
Shiro 加密
认证时使用如下:
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//加密方式
matcher.setHashIterations(1);//加密次数
customRealm.setCredentialsMatcher(matcher);//在realm中添加加密方式
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("cjl",password,"customRealm");//在自定义的Realm认证方式的中添加
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("cjl"));
如果需要加盐
Shiro整合Spring
步骤1、在web.xml中添加shiro的filter
<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>
步骤2、在spring的配置文件中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>
参数解释:
anon:不需要认证,直接可以访问
authc:需要认证后才可以访问
后面shiro过滤器部门详解
步骤3、创建SecurityManager对象
<!-- 创建SecurityManager对象--> <bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager"> <property name="realm" ref="customRealm"/> </bean>
步骤4、创建realm对象,可以使用自定义realm
<bean class="com.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>
步骤5、登陆代码如下:
@RequestMapping(value = "/sublogin",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();
}
try {
subject.checkPermission("user:delete");
} catch (AuthorizationException e) {
return String.format("没有权限%s", e.getMessage());
}
return "登录成功,拥有权限";
}
Shiro注解配置授权
步骤1、添加pom依赖
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
步骤2、在spring配置文件中添加shiro的aop
<aop:config proxy-target-class="true"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
步骤4、在需要进行认真的controller上添加注解
@RequiresRoles("admin")
@RequiresPermissions("user:delete")
Shiro过滤器
shiro内置的过滤器
认证相关的:anon(不需要认证),authBasic,authc(需要认证),user(需要当前存在用户),logout(需要登出)
权限相关:perms(权限格式["user:delete","user"add"]),roles(角色相关,用法同),ssl(要求安全协议:https),port(要求端口,格式同上)
自定义Filter
如果和授权相关,继承AuthorizationFilter,和认证相关则继承AuthenticatingFilter
代码示例:
public class RolesOrFilter extends AuthorizationFilter{
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest,servletResponse );
String[] roles = ((String[]) o);
if (roles == null && roles.length <= 0) {
for (String role : roles)
if (subject.hasRole(role)) {
return true;
}
return false;
} else {
return true;
}
}
}
在spring的配置文件中进行配置
1、添加该过滤器
<bean class="com.shiro.controller.filter.RolesOrFilter" id="rolesOrFilter"/>
2、将这个过滤器配置到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 /testRolesOr = rolesOr["user:delete","user:add"] /* = authc </value> </property> <property name="filters"> <util:map> <entry key="rolesOr" value-ref="rolesOrFilter"/> </util:map> </property> </bean>
完成。
Shiro会话管理和缓存管理
会话管理,shiro使用的是AbstractSessionDAO,自定义的时候需要继承这个类,实现其中的方法,比如采用redis的方法。
public class RedisSessionDao extends AbstractSessionDAO {
@Resource
private JedisUtil jedisUtil;
private final String SHIRO_SESSION_PREFIX= "test-session";
private byte[] getkey(String key){
return (String.format("%s%s", SHIRO_SESSION_PREFIX, key)).getBytes();
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = generateSessionId(session);
//绑定sessionId和session
assignSessionId(session,sessionId );
redisSaveSession(session);
return sessionId;
}
private void redisSaveSession(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 Session doReadSession(Serializable sessionId) {
if (sessionId==null) {
return null;
}
byte[] key = getkey(sessionId.toString());
byte[] value = jedisUtil.getkey(key);
return (Session) deserialize(value);
}
@Override
public void update(Session session) throws UnknownSessionException {
redisSaveSession(session);
}
@Override
public void delete(Session session) {
if (session==null || session.getId()==null) {
return;
}
jedisUtil.del(getkey(session.getId().toString()));
}
@Override
public Collection<Session> getActiveSessions() {
Set<byte[]> keys = jedisUtil.keys(SHIRO_SESSION_PREFIX);
Set<Session> sessions = new HashSet<>();
if (!isEmpty(keys)) {
for (byte[] key : keys) {
Session session = (Session) SerializationUtils.deserialize(key);
sessions.add(session);
}
return sessions;
} else {
return sessions;
}
}
}
在spring的配置文件中进行配置
<bean class="com.shiro.session.CustomSessionManager" id="sessionManager"> <property name="sessionDAO" ref="redisSessionDao"/> </bean> <bean class="com.shiro.session.RedisSessionDao" id="redisSessionDao"/>
<!-- 创建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="cacheManager"/> </bean>
使用redis的时候需要重新实现DefaultWebSessionManager,因为每个请求都会去向redis发送请求查询session,对redis造成很大的压力,重新实现DefaultWebSessionManager类,继承这个类,重载其中的retrieveSession方法,第一次请求的时候将session放到request域中。
缓存管理
缓存管理和会话管理类似,代码如下
public class RedisCacheManager implements CacheManager{
@Resource
private RedisCache redisCache;
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
return redisCache;
}
}
@Component
public class RedisCache<K,V> implements Cache<K,V> {
@Resource
private JedisUtil jedisUtil;
private final static String CACHE_PREFIX = "test-cache:";
private byte[] getkey(K k){
if(k instanceof String){
return (CACHE_PREFIX + k).getBytes();
}else{
return SerializationUtils.serialize(k);
}
}
@Override
public V get(K k) throws CacheException {
byte[] value = jedisUtil.getkey(getkey(k));
if(value!=null){
System.out.println("从redis中获取数据");
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.getkey(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的配置文件中进行配置
<bean class="com.shiro.cache.RedisCacheManager" id="cacheManager"/>
完成。
详细代码见:
https://github.com/caojinlin/shiroTest.git


浙公网安备 33010602011771号