从已有java web项目中学习shiro安全管理

用户安全控制模块问题陈述
安全管理一           security-管理员模块 后于后台运营管理-JRealm-身份认证和用户授权

安全管理二           member-基于shiro自定义过滤器
----------------------------------------------------------------------------------------------------------------------------------------------------------
对安全管理二的问题描述:
当会员进行充值、提现、获得奖金、消费、查看记录的操作时,过滤器检查会员是否是已登录的,若登录则保持请求允许请求操作;否则跳转到会员登录页面。
我遇到的问题:

问题一、如何和已有的jreaml结合呢。定义自己的过滤器,怎么做到当访问受保护的页面时,经过滤器呢。
因为 已经为后台会员管理模块的管理员 配置了shiro的 jpaRealm数据源,当要登录进入后台管理员模块时,都会去验证登录者的身份,当访问某一资源时,去验证改登陆者是否有被授予相应的权限。
所以 如果 我定义会员要访问的资源/m/account/*在该shiro的过滤下,同时会员登录的时候,在Subject中记录会员的信息。那么这样当登录时和访问资源时都会走jpaRealm这一过滤器。那这样这个本来为后台运营管理服务的User服务的数据源要添加member 会显得很复杂不合理。而且由于会员需要第三方登录,第三方登录时记录会员信息在Subject中,但当改会员访问受保护资源时,Subject又是全新的一个。
综上,重新自定义一个过滤器,专门只为过滤当会员访问受保护资源时会员是否登录的验证。

问题二、那么自定义一个shiro的过滤器,要不要使用到subject,不使用怎么保存用户在登录时的信息呢?
因为第三方登录时存放在subject中的会员数据在会员重新访问资源的时候,subject是新的一个,娶不到登录时的该会员的数据。业务描述:希望会员登录的时候,给一个标志性的数据给该会员,然后会员访问去受保护资源时,在过滤器中检测该会员有没有token,若有允许请求;否则跳回登录页面。
综上,建立一个表 id memberid token ,当会员登录成功时就记录下该会员的token;当在过滤器中获取请求中是否有token,该token是否是该member的。当会员下线时,删除该token。

问题三、在页面,登录成功后台返回的数据有memberid和token,把这些数据保存在localStory中。会员关闭网页事也可以处于登录状态。不过当会员要进行会员业务时,要重新让该会员登录。这样的话数据库就存在一个会员会有多个token。如何保证现在进行会员业务的就是会员本人呢?
①在过滤器中,我们有通过request中的token从数据中获取memberid的过程,和过取出来的memberid和request中的memberid一致,则表示是会员本人。否则提示重新登录。
②request中的memberid和当前登录的会员的memberid比较,若相等则是会员本人

问题四、如何保持当前登录的用户信息呢?如何在各层中不带任何参数的获取到当前登录的用户呢?
①保存在session?session失效时重新登录。但是每次要获取当前登录用户的时候都要传递参数request
②使用ThreadLocal把当前登录用户保存起来,这是一个线程级别的,问题是每次request都是新的一个线程?选择保存request在ThreadLocal,然后获取request的session,虽然每次都是新的线程,但是如果是同个会话,还是可以获取到登录时的会员信息。
综上,添加身份验证过滤器,当会员进行会员业务时,都要经过这层过滤器,如果当前登录会员和request中的memberid是同一个,则允许访问,否则调回登录页面。

问题五、如何调回登录页面呢?因为app调用的接口访问的都是json数据,如何在页面上load出登录的view呢?
在过滤器中,如何验证不通过就会跳到登录页面
----
<bean id="memberAuthenticationFilter" class="com.ole.platform.security.shiro.realm.MemberAuthenticationFilter">
    <property name="loginUrl" value="/m/member/login"/>
</bean>
<bean id="identityAuthenticationFilter" class="com.ole.platform.security.shiro.realm.IdentityAuthenticationFilter">
    <property name="loginUrl" value="/m/member/login"/>
</bean>
----
@RequestMapping(value = "/login", method = RequestMethod.GET)
@ResponseBody
public String login(HttpServletResponse response){
    response.setHeader("x-expired","1");
    return new Message("请您先登录 !").toJson();
}
--思路:可以在后台验证身份失败时,在header中设置token失效的信息,在页面可以通过验证请求头中的信息是否有token失效的信息,若有就跳到登录页面
over
使用shiro进行安全管理实现
一、web.xml中配置shiro拦截
<!-- Shiro 配置相关 -->
<!-- 过滤器 'shiroFilter'  applicationContext.xml-->
<!-- 委托DelegatingFilterProxy Spring进行代理,但是使用targetFilterLifecycle=true表明使用shiro过滤器 -->
<filter>
   <filter-name>shiroFilter</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   <!-- <async-supported>true</async-supported> <init-param> <param-name>targetFilterLifecycle</param-name> 
      <param-value>true</param-value> </init-param> -->
</filter>

<filter-mapping>
   <filter-name>shiroFilter</filter-name>
   <url-pattern>/*</url-pattern>

</filter-mapping>
 二、配置spring-shiro-web.xml和spring-mvc-shiro.xml文件
<!-- ShiroWeb过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" depends-on="memberAuthenticationFilter,identityAuthenticationFilter">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="/admin/login"/>

    <!--other-->
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    <property name="filters">
        <util:map>
            <entry key="MyFilter" value-ref="memberAuthenticationFilter"/>
            <entry key="Identity" value-ref="identityAuthenticationFilter"/>
            <entry key="authc" value-ref="formAuthenticationFilter"/>

        </util:map>

    </property>
    <property name="filterChainDefinitions">
        <value>
            /account/dowith/* = MyFilter
            /m/account/* = MyFilter,Identity
            /member/login = anon

            <!--/member/test = role[admin]-->
            /admin/login = anon
            /admin/home = authc
            <!--用户URL配置-->
            /security/user/list = authc,perms["user:view"]
            /security/role/list = authc,perms["user:view"]
            /security/user/create = authc,perms["user:*"]
            /security/user/edit = authc,perms["user:*"]
            /security/user/delete = authc,perms["user:*"]
            <!--会员信息URL配置 运营管理员 和 平台管理员-->
            /admin/member/create = authc,perms["member:*"]
            /admin/member/edit = authc,perms["member:*"]
            /admin/member/list = authc,perms["member:*"]
            /admin/member/delete = authc,perms["member:*"]
            /admin/member/check = authc,perms["member:*"]
            <!--会员交易记录URL配置  财务管理员 和 平台管理员-->
            /admin/member/transition = authc,perms["memberAccount:*"]

        </value>
    </property>
</bean>
三、JRealm数据源
身份验证
/*
 * 认证信息处理
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
String username = token.getPrincipal().toString();
User user = this.jpaRealmRepository.findUserByName(username);

if (null == user)
{
   log.error("没有相关用户!");
   throw new UnknownAccountException();
}
授权信息处理
/*
 *授权信息处理
 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
{
//获取登录时输入的用户名
String loginName=(String) principals.fromRealm(getName()).iterator().next();
//到数据库获取此用户
User user=this.jpaRealmRepository.findUserByName(loginName);

System.out.println("jpaRealm authorization ...");

if(user!=null){
    //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission    SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();

    //用户的角色对应的所有权限,如果只使用角色定义访问权限
    Collection<Role> roleList=user.getRoles();
    for (Role role : roleList) {
        System.out.println("Authorization.permission="+role.getPermissions());
        info.addStringPermissions(role.getPermissions());
    }
    return info;
}
return null;
四、自定义过滤器
访问/m/account/*下的资源时,过滤器验证用户是否登录
第一个过滤器memberAuthorizationFilter
String token = httpRequest.getHeader("token")!= null ? httpRequest.getHeader("token") : request.getParameter("token");

long memberId = (!StringUtils.isEmpty(request.getParameter("memberId"))) ? Long.parseLong(request.getParameter("memberId")) : 0;
//拦截
if (!org.springframework.util.StringUtils.isEmpty(token)) {
    System.out.println("token---"+token);
    List<String> tokens = tokenService.findAllToken();
    if (tokens != null && tokens.contains(token)) {

        if(tokenService.findMemberId(token) == memberId) {
            System.out.println("query2------" + httpRequest.getQueryString());
            return true;
        }else {
            //该用户身份错误

            saveRequestAndRedirectToLogin(request, response);
        }
    } else {
        //该用户没有登录过
        saveRequestAndRedirectToLogin(request, response);
    }
} else {
    //该用户还没登录
    saveRequestAndRedirectToLogin(request, response);
}


return false;
若该过滤器通过则继续下一个过滤器,验证进行会员业务的是否为当前登录用户

protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
    System.out.println("identity filter ");
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    long memberId = (!StringUtils.isEmpty(httpRequest.getParameter("memberId"))) ? Long.parseLong(request.getParameter("memberId")) : 0;

        //新的请求时新的一个线程,但是新的请求和上一次请求时同一个会话中 可以得到当前登陆的用户
        ThreadMember.setMember((HttpServletRequest) request);
        Object o = ThreadMember.getMember().getSession().getAttribute("currentMemberId");
        System.out.println("identity id is " + o);
        if(o != null) {
            if((long)o == memberId) {
                return true;
            }
        }else {
            //该用户还没登录
            saveRequestAndRedirectToLogin(request, response);
        }
    return false;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值