springboot整合shiro和session的详细过程和自定义登录拦截器

shiro是一个安全框架,用于认证和授权,我觉得与springsecurity相比它上手更容易,同时如果是简单的登录拦截也可以用登录拦截器实现,下面先进行springboot整合 shiro的过程

1.shiro依赖

      <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>
               <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.1</version>
        </dependency>

2.shiro配置

配置安全管理器:SecurityManager
Authenticator 的职责是验证用户帐号,是ShiroAPI 中身份验证核心的入口点:如果验证成功,将返回AuthenticationInfo验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException异常
•SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm 进行验证,验证规则通过AuthenticationStrategy接口指定

  /**
     * 安全管理器
     */
    @Bean
    public SecurityManager securityManager(UserRealm userRealm)
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        // 记住我
        securityManager.setRememberMeManager(rememberMe ? rememberMeManager() : null);
        // 注入缓存管理器;
        securityManager.setCacheManager(getEhCacheManager());
        // session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }
    

哪些url是需要拦截的,哪些是不需要拦截的,登录页面、登录成功页面的url、自定义的Realm等这些信息需要设置到Shiro中

shiro过滤器配置:

 /**
     * Shiro过滤器配置
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败,则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 权限认证失败,则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        // Shiro连接约束配置,即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 对静态资源设置匿名访问
        filterChainDefinitionMap.put("/favicon.ico**", "anon");
 //加入自己的路径和访问权限
        // 系统权限列表
        // filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());

        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put("onlineSession", onlineSessionFilter());
        filters.put("syncOnlineSession", syncOnlineSessionFilter());
        filters.put("captchaValidate", captchaValidateFilter());
        filters.put("kickout", kickoutSessionFilter());
        // 注销成功,则跳转到指定页面
        filters.put("logout", logoutFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 所有请求需要认证
        filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

安全管理器关联自己的Realm

  @Bean(name="security")
    public DefaultWebSecurityManager getDefaultManager(@Qualifier("realm")UserRealm userRealm){
     DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
     //关联自己的realm
        defaultSecurityManager.setRealm(userRealm);
        return defaultSecurityManager;
    }
    @Bean(name = "realm")
    //创建realm对象
    public UserRealm userRealm(){
        return new UserRealm();
    }

关联自定义的其他管理器

    @Bean
    public SecurityManager securityManager(UserRealm userRealm)
    {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        // 记住我
        securityManager.setRememberMeManager(rememberMe ? rememberMeManager() : null);
        // 注入缓存管理器;
        securityManager.setCacheManager(getEhCacheManager());
        // session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

 /**
     * 缓存管理器 使用Ehcache实现
     */
    @Bean
    public EhCacheManager getEhCacheManager()
    {
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");
        EhCacheManager em = new EhCacheManager();
        if (StringUtils.isNull(cacheManager))
        {
            em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));
            return em;
        }
        else
        {
            em.setCacheManager(cacheManager);
            return em;
        }
    }

缓存:Shiro内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(如Realm)是否实现了CacheManagerAware并自动注入相应的CacheManager。
Shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了缓存的一些基础实现;
•AuthenticatingRealm及AuthorizingRealm也分别提供了对AuthenticationInfo和AuthorizationInfo信息的缓存。
Session 缓存
•如SecurityManager实现了SessionSecurityManager,其会判断SessionManager是否实现了CacheManagerAware接口,如果实现了会把CacheManager设置给它。
•SessionManager也会判断相应的SessionDAO(如继承自CachingSessionDAO)是否实现了CacheManagerAware,如果实现了会把CacheManager设置给它
•设置了缓存的SessionManager,查询时会先查缓存,如果找不到才查数据库。

自定义会话工厂:

//自定义sessionFactory会话
@Component
public class OnlineSessionFactory implements SessionFactory
{
    @Override
    public Session createSession(SessionContext initData)
    {
        OnlineSession session = new OnlineSession();
        if (initData != null && initData instanceof WebSessionContext)
        {
            WebSessionContext sessionContext = (WebSessionContext) initData;
            HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest();
            if (request != null)
            {
                UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
                // 获取客户端操作系统
                String os = userAgent.getOperatingSystem().getName();
                // 获取客户端浏览器
                String browser = userAgent.getBrowser().getName();
                session.setHost(IpUtils.getIpAddr(request));
                session.setBrowser(browser);
                session.setOs(os);
            }
        }
        return session;
    }
}

3.登陆时记录用户信息

在控制器层:

   @ApiOperation(value="登录")
@PostMapping("/login")
    public ResponseResult<User> toLogin(@ApiParam(name="用户对象",value="传入json格式",required=true)LoginForm loginForm)
{
   

    User user = userService.selectUserByLoginName(loginForm.getUserName(),loginForm.getPassword(), false);
  
    if (user != null) {
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassword(), false);
    Subject subject = SecurityUtils.getSubject();
subject.login(token);
return ResponseResult.success();}
    else return ResponseResult.error();
}

4.shiro一些工具类的学习

SecurityUtils.getSubject()是每个请求创建一个Subject, 并保存到ThreadContext的resources(ThreadLocal<Map<Object, Object>>)变量中,也就是一个http请求一个subject,并绑定到当前线程。
subject.login()登陆认证成功后,下一次请求如何知道是那个用户的请求呢?
内部原理:1个请求1个Subject原理:由于ShiroFilterFactoryBean本质是个AbstractShiroFilter过滤器,所以每次请求都会执行doFilterInternal里面的createSubject方法。
源码:


public interface Subject {
    Object getPrincipal();

    PrincipalCollection getPrincipals();

    boolean isPermitted(String var1);

    boolean isPermitted(Permission var1);
    ...省略其他方法}
    

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
    Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;

    void logout(Subject var1);

    Subject createSubject(SubjectContext var1);
}

public abstract class SecurityUtils {
    private static volatile SecurityManager securityManager;

    public SecurityUtils() {
    }

    public static Subject getSubject() {
        Subject subject = ThreadContext.getSubject();
        if (subject == null) {
            subject = (new Builder()).buildSubject();
            ThreadContext.bind(subject);
        }

        return subject;
    }

    public static void setSecurityManager(SecurityManager securityManager) {
        SecurityUtils.securityManager = securityManager;
    }

    public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
        SecurityManager securityManager = ThreadContext.getSecurityManager();
        if (securityManager == null) {
            securityManager = SecurityUtils.securityManager;
        }

        if (securityManager == null) {
            String msg = "No SecurityManager accessible to the calling code, either bound to the " + ThreadContext.class.getName() + " or as a vm static singleton.  This is an invalid application configuration.";
            throw new UnavailableSecurityManagerException(msg);
        } else {
            return securityManager;
        }
    }
}

shiro内置的session
session.setAttribute(“username”,username)就是将username保存到session中,session的key值为username,其信息(value)为username,或者引用值。这样以后可以通过session.getAttribute(“username”)的方法来获取这个对象。通常,当用户已经登录系统后,就可以在session中存储一个用户信息对象,伺候可以随时从session中将这个对象取出来进行一些操作,比如身份验证等等。

request.getSession()可以获得HttpSession类型的对象,通常称之为session对象,session对象的作用域为一次会话,通常浏览器不关闭,保存的值就不会消失,当然也会出现session超时。服务器里面可以设置session的超时时间,web.xml中有一个session time out的地方,tomcat默认为30分钟。
session. setAttribute(“key”,value)是session设置值的方法,原理同Map集合。
getAttribute的返回值类型是Object,需要向下转型,转成你的userName类型的。比如,String session1= (String)session.getAttribute(“student”) ;

5.自定义登录拦截器

这种办法不需要引入依赖,只需要继承HandlerInterceptor 即可 实现非常简单:

@Slf4j
public class UserLoginInterceptor implements HandlerInterceptor {

	/**
	 * true 表示继续流程,false表示中断
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		log.info("preHandle...");
		User user = (User) request.getSession().getAttribute(MallConst.CURRENT_USER);
		if (user == null) {
			log.info("user=null");
			throw new UserLoginException();

		}
		return true;
	}
}


然后定义一个配置类,启动时springboot便能进行自动配置

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new UserLoginInterceptor())
				.addPathPatterns("/**")//自己添加需要拦截的路径
				.excludePathPatterns("/error", "/user/login", "/user/register", "/categories",  "/products/*");//哪些路径不需要拦截
	}
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值