学了好几天了,一直说做笔记做笔记,总感觉没时间其实是拖延时间而已。以后学习之后都会总结。
1.导jar包
<!-- shiro权限管理版本-->
<shiro.version>1.2.2</shiro.version>
<!-- shiro权限管理的jar包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<!-- shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
</dependency>
2.配置shiro拦截器在项目的web.xml中配置例如
<!-- shiro拦截器的配置 -->
<filter>
<filter-name>shiroFileter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFileter</filter-name>
<!-- 使用[/*]匹配所有请求,保证所有的可控请求都经过Shiro的过滤 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
3.先自定义Realm毕竟登录验证和权限校验都要在这里面进行。我这里只做了授权没有做认证,也许是懒把。
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private indexservice indexservice;
/*
* 做授权
*授权查询回调函数, 进行鉴权但缓存中无用户的授权信
*息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//说一下思路把,得到这个用户名,去数据库或者缓存里面查询组装set集合返回。
String username=(String) getAvailablePrincipal(principals);
// TODO Auto-generated method stub
//返回数据
System.out.println("doGetAuthorizationInfo");
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
//authorizationInfo.setObjectPermissions(objectPermissions);设置权限集合
//authorizationInfo.setRoles(roles);设置角色
return authorizationInfo;
}
/*
* 做认证使用的方法
* 获取用户角色信息和权限信息,一代后续进行访问控制
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
String username =(String) token.getPrincipal();//得到用户名
//通过用户名获取密码
String password=getPasswordByUserName(username);
SimpleAuthenticationInfo authenticationInfo =new SimpleAuthenticationInfo(username,password,getName());
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("田"));//加盐
return authenticationInfo;
}
private String getPasswordByUserName(String username) {
// TODO Auto-generated method stub
System.out.println("从数据查询-----------------------");
return indexservice.getUserBypassword(username);
}
}
4.配置shiro,我是新建了一个applicationContext-shiro.xml来配置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" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--
loginUrl:没有登录的用户请求需要登录的页面时自动跳转到登录页面。
unauthorizedUrl:没有权限默认跳转的页面,登录的用户访问了没有被授权的资源自动跳转到的页面。
其他的一些配置,如下:
successUrl:登录成功默认跳转页面,不配置则跳转至”/”,可以不配置,直接通过代码进行处理。
securityManager:这个属性是必须的,配置为securityManager就好了。
filterChainDefinitions:配置过滤规则,从上到下的顺序匹配。
-->
<context:component-scan base-package="com.taotao.controller.shirocontroller"></context:component-scan>
<!--创建shiroFilter对象-->
<bean id="shiroFileter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="/index.action"></property>
<property name="unauthorizedUrl" value="/403"></property>
<property name="filterChainDefinitions">
<!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->
<!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 -->
<!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->
<value>
/index.action=anon
/shiroindext.action=anon
/*=authc
</value>
</property>
</bean>
<!--创建shiro需要的对象-->
<!--创建 securityManager 对象-->
<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<property name="realm" ref="realm"/>
<property name="sessionManager" ref="sessionManager"></property>
<property name="cacheManager" ref="cacheManager"></property>
<property name="rememberMeManager" ref="rememberMeManager"></property>
</bean>
<!--创建自定义realm对象-->
<bean class="com.taotao.controller.shirocontroller.ShiroRealm" id="realm">
<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="2"/>
</bean>
<!-- 创建自定义sessionManager 解决问题多次去redis查询数据 session管理 -->
<bean id="sessionManager" class="com.taotao.controller.shirocontroller.CustomSessionManager">
<property name="sessionDAO" ref="redisSessionDao"></property>
</bean>
<bean id="redisSessionDao" class="com.taotao.controller.shirocontroller.RedisSessionDao">
</bean>
<bean class="com.taotao.controller.shirocontroller.RedisCacheManage" id="cacheManager">
</bean>
<!-- shiro 自动登录配置-->
<bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="rememberMeManager">
<property name="cookie" ref="cookie"></property>
</bean>
<bean id="cookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"></constructor-arg>
<!-- 设置登录超时时间 -->
<property name="maxAge" value="200000"></property>
<property name="httpOnly" value="true" />
</bean>
</beans>
5.applicationContext-shiro.xml这个配置文件之中有id="sessionManager"这个是做session管理的一个类本来shiro自带一个类,但是他们看视频说,这个类在查询session的时候会多次查找会导致性能下降,确实有这个问题。
public class CustomSessionManager extends DefaultWebSessionManager {
/**
* 重写方法,优先从request对象中读取session,减轻redis压力
* @param sessionKey
* @return
* @throws UnknownSessionException
*/
@Override
protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
//取到sessionId
Serializable sessionId = getSessionId(sessionKey);
ServletRequest request = null;
//获取request
if (sessionKey instanceof WebSessionKey){
request = ((WebSessionKey) sessionKey).getServletRequest();
}
//获取session
Session session = null;
if (request != null && sessionId != null){
session = (Session) request.getAttribute(sessionId.toString());
if (session != null){
return session;
}
}
session = super.retrieveSession(sessionKey);
if (request != null && sessionId != null) {
request.setAttribute(sessionId.toString(), session);
}
return session;
}
}
6.applicationContext-shiro.xml这个配置文件之中有id="redisSessionDao"这个类这是做session缓存的一个类。
/**
* AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话ID等
* @ClassName: RedisSessionDao
* @Description:
* @author 14729
* @date 2020年1月9日
*
*/
public class RedisSessionDao extends AbstractSessionDAO {
@Autowired
private JedisClientPool jedis;
/**
* 自定义生成sessionId时加上前缀。
*/
private final String SHIRO_SESSION_PREFIX = "shiro-session:";
/**
* @param key
* @return
*/
private byte[] getKey(String key) {
return (SHIRO_SESSION_PREFIX + key).getBytes();
}
public void saveSession(Session session) {
if(session!=null&&session.getId()!=null) {
byte[] value = SerializationUtils.serialize(session);
jedis.setbyte(getKey(session.getId().toString()),value);
jedis.expirebyte(getKey(session.getId().toString()),1000);
}
}
@Override
public void update(Session session) throws UnknownSessionException {
// TODO Auto-generated method stub
saveSession(session);
}
@Override
public void delete(Session session) {
if(session!=null&& session.getId()!=null) {
jedis.delbyte(getKey(session.getId().toString()));
}
}
/*
* 获取所有的session
* (non-Javadoc)
* @see org.apache.shiro.session.mgt.eis.SessionDAO#getActiveSessions()
*/
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions=new HashSet<Session>();
Set<String> keys = jedis.keys(SHIRO_SESSION_PREFIX);
if(CollectionUtils.isEmpty(keys))
return sessions;
for (String string : keys) {
Session session= (Session) SerializationUtils.deserialize(jedis.getbyte(string.getBytes()));
sessions.add(session);
}
return sessions;
}
/* 创建session,返回sessionid
* @see org.apache.shiro.session.mgt.eis.AbstractSessionDAO#doCreate(org.apache.shiro.session.Session)
*/
@Override
protected Serializable doCreate(Session session) {
System.out.println("创建ssession给缓存里面保存session");
Serializable sessionid=generateSessionId(session);//根据session生成sessionId
assignSessionId(session, sessionid); //将sessionId和session进行捆绑
saveSession(session); //保存session
return sessionid;
}
/**
* 根据sessionId返回session
*
* @param sessionId
* @return
*/
@Override
protected Session doReadSession(Serializable sessionId) {
if (StringUtils.isEmpty(sessionId)) {
return null;
}
byte[] key = getKey(sessionId.toString());
byte[] value = jedis.getbyte(key);
if(StringUtils.isEmpty(value))
return null;
return (Session) SerializationUtils.deserialize(value);
}
}
7 配置文件中id="cacheManager"这个类是做会话管理的。
public class RedisCacheManage implements CacheManager{
@Autowired
private RedisCache<?, ?> redisCache;
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
// TODO Auto-generated method stub
return (Cache<K, V>) redisCache;
}
}
8.RedisCacheManage这个类里面的 RedisCache是做会话缓存管理的
@Component
public class RedisCache<K,V> implements Cache<K,V>{
private final String CACHE_PREFIX="cache-session";
@Autowired
private JedisClientPool jePool;
private byte[] getKey(K k) {
if(k instanceof String) {
return (CACHE_PREFIX+k).getBytes();
}
return SerializationUtils.serialize(k);
}
@Override
public V put(K key, V val) {
System.out.println("");
byte[] bs = getKey(key);
byte[] value=SerializationUtils.serialize(val);
jePool.setbyte(bs, value);
jePool.expirebyte(bs, 1000);
return val;
}
@Override
public V get(K key) {
byte[] bs = jePool.getbyte(getKey(key));
if(bs!=null)
return (V) SerializationUtils.deserialize(bs);
return null;
}
@Override
public V remove(K key) {
byte[] bs = getKey(key);
byte [] value=jePool.getbyte(bs);
jePool.delbyte(bs);
if(value==null)
return null;
return (V) SerializationUtils.deserialize(value);
}
@Override
public void clear() {
// TODO Auto-generated method stub
}
@Override
public int size() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Set<K> keys() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<V> values() {
// TODO Auto-generated method stub
return null;
}
}
9,上面JedisClientPool 这个类是对redis做一些增删改查的。这个类就不给了,其实就是对JedisPool这类进行操作。
10.基本都配置完成了。接着定义一个Controller
@Controller
public class shiroController {
@Autowired
private RedisSessionDao redisSessionDao;
@RequestMapping(value="/index")
public String shiroIndex() {
return "NewFile";
}
@RequestMapping(value="/shiroindext",method= { RequestMethod.GET, RequestMethod.POST })
public String subLogin(User user) {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated() && subject.isRemembered()) {//判断cookie是否过期。在配置文件里面有配置,自动登录
System.out.println("没有使用cookie登录");
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try {
System.err.println("获取的值是"+user.isRememberMe());
token.setRememberMe(user.isRememberMe());
subject.login(token);
} catch (AuthenticationException e) {
return "nopower";
}
// subject.checkRole("admin1");
return "Main";
}else {
System.out.println("使用cookie登录=======》");
return "Main";
}
}
}
11.q
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Insert title here</title>
</head>
<body>
<form action="shiroindext.action" method="post">
<a>来了</a>
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br>
是否选中<input type="checkbox" name="rememberMe"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>