Shiro入门到整合redis

1、Shiro整体架构认识

用户操作进入Security Manager ,Security Manager 通过Authentication认证器想Reaml获取认证数据,通过Authorizer向Reaml获取权限数据

2、Shiro认证和授权

代码示意:

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采用的是配置文件的格式
//新建IniReam
IniRealm iniRealm = new IniRealm("classpath:user.ini");

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
posted @ 2018-05-06 22:46  司霖  阅读(550)  评论(0编辑  收藏  举报