Springmvc集成Shiro实现权限管理

本文详细介绍了如何将SpringMVC与Apache Shiro整合,用于实现权限管理。涵盖了Shiro的默认过滤器配置,如authc、roles和perms,以及Shiro在JSP中的标签用法,包括guest、user、authenticated等。同时,文章还讨论了Ehcache缓存的配置选项。整个集成过程包括工程结构规划、相关jar包导入、web.xml、springmvc.xml和applicationContext-shiro.xml的配置,以及自定义Realm、User、Role、Permission、UserService、SpringUtil等相关类的创建。此外,还涉及登录和主界面的页面设计。
摘要由CSDN通过智能技术生成
Springmvc集成Shiro实现权限管理

源代码下载:http://download.csdn.net/detail/u013147600/9062175

Shiro是一个安全框架,他可以集成其他开发开发框架 如:Springmvc,实现用户身份认证、权限管理等等功能, shiro详细的介绍也就不讲了,这里 给出一些关键的知识点吧:

知识点:

shiro中默认的过滤器

过滤器名称 过滤器类 描述
anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名过滤器
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 如果继续操作,需要做对应的表单验证否则不能通过
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 基本http验证过滤,如果不通过,跳转屋登录页面
logout org.apache.shiro.web.filter.authc.LogoutFilter 登录退出过滤器
noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter 没有session创建过滤器
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 权限过滤器
port org.apache.shiro.web.filter.authz.PortFilter 端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter http方法过滤器,可以指定如post不能进行访问等
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 角色过滤器,判断当前用户是否指定角色
ssl org.apache.shiro.web.filter.authz.SslFilter 请求需要通过ssl,如果不是跳转回登录页
user org.apache.shiro.web.filter.authc.UserFilter 如果访问一个已知用户,比如记住我功能,走这个过滤器

 <!-- 访问 /member/queryMyUserInfo路径需要进行身份认证,并且只有当用户的角色为"member"且权限为"member:delete"时才可以访问该路径-->

     /member/queryMyUserInfo=authc,roles[member],perms[member:delete]  


Shiro相应的jsp标签:

jsp页面中导入:<%@ taglib prefix= "shiro"  uri= "http://shiro.apache.org/tags"  %>
  • guest标签  验证当前用户是否为“访客”,即未认证(包含未记住)的用户
  • user标签  认证通过或已记住的用户
  • authenticated标签  已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 
  • notAuthenticated标签  未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。
  • principal 标签  输出当前用户信息,通常为登录帐号信息 
  • hasRole标签 验证当前用户是否属于该角色 
  • lacksRole标签  与hasRole标签逻辑相反,当用户不属于该角色时验证通过 
  • hasAnyRole标签  验证当前用户是否属于以下任意一个角色。
  • hasPermission标签  验证当前用户是否拥有制定权限 
  • lacksPermission标签 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过 

Ehcache缓存:

<!--
  •   name:Cache的唯一标识  
  •   maxElementsInMemory:内存中最大缓存对象数  
  •   maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大  
  •   eternal:Element是否永久有效,一但设置了,timeout将不起作用 
  •   overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中  
  •   timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大  
  •   timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大  
  •   diskPersistent:是否缓存虚拟机重启期数据  
  •   diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒  
  •   diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区  
  •   memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用) 
 -->

springmvc集成shiro项目的配置:

工程结构:


导入jar包:


1.web.xml配置:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>SecurityShiro</display-name>
  <!-- 执行顺序ServletContext,context-param , listener ,filter ,servlet -->
    <!-- 指定Spring的配置文件 -->  
    <!-- 否则Spring会默认从WEB-INF下寻找配置文件,contextConfigLocation属性是Spring内部固定的 -->  
    <!-- 通过ContextLoaderListener的父类ContextLoader的第120行发现CONFIG_LOCATION_PARAM固定为contextConfigLocation -->  
    <context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath:applicationContext-shiro.xml</param-value>  
    </context-param>  
  
    <!-- 防止发生java.beans.Introspector内存泄露,应将它配置在ContextLoaderListener的前面 -->  
    <!-- 详细描述见http://blog.csdn.net/jadyer/article/details/11991457 -->  
    <listener>  
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>  
    </listener>  
      
    <!-- 实例化Spring容器 -->  
    <!-- 应用启动时,该监听器被执行,它会读取Spring相关配置文件,其默认会到WEB-INF中查找applicationContext.xml -->  
    <!-- http://starscream.iteye.com/blog/1107036 -->  
    <!-- http://www.davenkin.me/post/2012-10-18/40039948363 -->  
    <!-- WebApplicationContextUtils.getWebApplicationContext() -->  
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
  
    <!-- 解决乱码问题 -->  
    <!-- forceEncoding默认为false,此时效果可大致理解为request.setCharacterEncoding("UTF-8") -->  
    <!-- forceEncoding=true后,可大致理解为request.setCharacterEncoding("UTF-8")和response.setCharacterEncoding("UTF-8") -->  
    <filter>  
        <filter-name>SpringEncodingFilter</filter-name>  
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
        <init-param>  
            <param-name>encoding</param-name>  
            <param-value>UTF-8</param-value>  
        </init-param>  
        <init-param>  
            <param-name>forceEncoding</param-name>  
            <param-value>true</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>SpringEncodingFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>  
      
    <!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 -->  
    <!-- 这里filter-name必须对应applicationContext.xml中定义的<bean id="shiroFilter"/> -->  
    <!-- 使用[/*]匹配所有请求,保证所有的可控请求都经过Shiro的过滤 -->  
    <!-- 通常会将此filter-mapping放置到最前面(即其他filter-mapping前面),以保证它是过滤器链中第一个起作用的 -->  
    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
        <init-param>  
            <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
            <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>  
  
    <!-- SpringMVC核心分发器 -->  
    <servlet>  
        <servlet-name>SpringMVC</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:springmvc.xml</param-value>  
        </init-param>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>SpringMVC</servlet-name>  
        <url-pattern>/</url-pattern>  
    </servlet-mapping>  
</web-app>

2.springmvc.xml配置:

<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd 
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
 http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<!-- 注解驱动 -->
<mvc:annotation-driven/>
<!-- 自动扫描包 -->
<context:component-scan base-package="com" />
<!-- 静态文件防止过滤 -->
<!-- 	<mvc:resources location="res" mapping="/res/**"/> -->
 	<mvc:default-servlet-handler/>  

<!-- 默认访问跳转到登录页面(即定义无需Controller的url<->view直接映射) -->  
<mvc:view-controller path="/" view-name="forward:/login.jsp"/>

<!-- mvc返回页面的配置 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 模板路径为WEB-INF/pages/ -->
<property name="prefix">
<value>/</value>
</property>
<!-- 视图模板后缀为.JSP -->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

</beans>

3.applicationContext-shiro.xml 配置(shiro配置)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd">

<!-- 自定义的Realm -->
<bean id="myRealm" class="com.authc.shiro.MyRealm"></bean>

<!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session -->  
    <!-- 即<property name="sessionMode" value="native"/>,详细说明见官方文档 -->  

<!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 --> 
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"></property>
</bean>

 <!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->  
    <!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 -->  

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的安全接口,必须配置的属性 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 登录时的链接 -->
<property name="loginUrl" value="/member/login"></property>

<!-- 登录成功后跳转到的链接 -->
<!-- <property name="successUrl" value="/member/main"></property> -->

 <!-- 用户访问未对其授权的资源时,所显示的连接 -->  
<property name="unauthorizedUrl" value="/member/login"></property>
<!-- /error.jsp -->
  <!-- Shiro连接约束配置,即过滤链的定义 -->  
        <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 -->  
        <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->  
        <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 -->  
        <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->  

<property name="filterChainDefinitions">
<value>
/member/login=anon
/member/getVerifyCodeImage=anon
/member/tologin=anon
<!-- 访问 /member/queryMyUserInfo路径需要进行身份认证,并且只有当用户的角色为"member"且权限为"member:delete"时才可以访问该路径
 /member/queryMyUserInfo=authc,roles[member],perms[member:delete] 
-->
/member/queryMyUserInfo=authc
/admin/**=authc,perms[admin:manage] 
</value>

</property> 
</bean>

<!-- 保证实现了shiro内部Lifecycle函数的bean的执行  (生命周期)-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
</beans>

4. MyRealm.java

package com.authc.shiro;

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import com.authc.entity.Permission;
import com.authc.entity.Role;
import com.authc.entity.User;
import com.authc.service.PermissionService;
import com.authc.service.RoleService;
import com.authc.service.UserService;
import com.authc.service.Impl.UserServiceImpl;

public class MyRealm extends AuthorizingRealm {

 private UserService userService =new UserServiceImpl();
 
 /* (non-Javadoc)
  * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
  * 为当前登录的Subject授予角色和权限
     * @see 经测试:本例中该方法的调用时机为需授权资源被访问时
     * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
  */
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

  //获取当前登录的用户名
  String username =(String) super.getAvailablePrincipal(principals);
  //根据用户名从数据库中获取用户信息
  User user =userService.queryUserByUserName(username);
 
  List<String> roleList = new ArrayList<String>();
  List<String> permList = new ArrayList<String>();
  System.out.println("对当前用户:["+username+"]进行授权!");
  if(null!=user)
  {
<!--因为我这里的的role和Permission 只是简单的实现,每个用户只有一个角色以及对于一个权限,所以不需要去遍历,实际工作中需要把用户的多个角色和多个权限都添加到 List<String> roleList和List<String> permList中 -->

   if(user.getRole()!=null && user.getRole().getRoleName()!=null)
   {
    roleList.add(user.getRole().getRoleName());
   
    if(user.getRole().getPermission()!=null && user.getRole().getPermission().getPermName()!=null)
    {
     permList.add(user.getRole().getPermission().getPermName());
     
     SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
     info.addRoles(roleList);
     info.addStringPermissions(permList);
     
     return info;
    }
   }
   
  }else
  {
   
   throw new AuthorizationException();
  }

  //若该方法什么都不做直接返回null的话,就会导致任何用户访问/admin/listUser.jsp时都会自动跳转到unauthorizedUrl指定的地址  
 
  return null;
 }

 /* (non-Javadoc)
  * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) 
  * 验证当前登录的Subject
     * @see 经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时
  */
 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(
   AuthenticationToken authToken) throws AuthenticationException {
  
  UsernamePasswordToken token =(UsernamePasswordToken) authToken;
 
  System.out.println("验证当前Subject时获取到token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  
 
  User user =userService.queryUserByUserName(token.getUsername());
 
  if(user!=null)
  {
   AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),getName());
//将当前用户设置到Session中去以便获取当前用户信息
   this.setSession("currentUser", user);
   
   return info;
  }
   
  return null;
 }

 /**
     * 将一些数据放到ShiroSession中,以便于其它地方使用
     * @see 比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到
     */  
 private void setSession(Object key,Object value)
 {
  Subject currSubject =SecurityUtils.getSubject();
  if(currSubject!=null)
  {
   Session session = currSubject.getSession();
   System.out.println("Session默认超时时间为[" + session.getTimeout() + "]毫秒");  
   
   if(session!=null)
   {
    session.setAttribute(key, value);
   }
  }
 
 }
}

User.java

package com.authc.entity;

/**
 * @author lyx
 *
 * 2015-8-26上午8:42:15
 *
 *com.authc.entity.User
 *
 */
public class User {

 private String username;
 private String password;
 private Role role;
 public String getUsername() {
  return username;
 }
 public String getPassword() {
  return password;
 }
 public Role getRole() {
  return role;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public void setRole(Role role) {
  this.role = role;
 }
 public User(String username, String password, Role role) {
  super();
  this.username = username;
  this.password = password;
  this.role = role;
 }
 public User() {
  super();
 }
 
 
}

Role.java

package com.authc.entity;

/**
 * @author lyx
 *
 * 2015-8-26上午8:43:15
 *
 *com.authc.entity.Role
 *
 */
public class Role {

 private String roleName;
 private Permission permission;
 public String getRoleName() {
  return roleName;
 }
 public Permission getPermission() {
  return permission;
 }
 public void setRoleName(String roleName) {
  this.roleName = roleName;
 }
 public void setPermission(Permission permission) {
  this.permission = permission;
 }
 public Role() {
  super();
 }
 
 
}

Permission.java

package com.authc.entity;

/**
 * @author lyx
 *
 * 2015-8-26上午8:44:26
 *
 *com.authc.entity.Permission
 *
 */
public class Permission {

 private int permId;
 private String permName;
 
 public int getPermId() {
  return permId;
 }
 public String getPermName() {
  return permName;
 }
 public void setPermId(int permId) {
  this.permId = permId;
 }
 public void setPermName(String permName) {
  this.permName = permName;
 }
 
 public Permission(int permId, String permName) {
  super();
  this.permId = permId;
  this.permName = permName;
 }
 public Permission() {
  super();
 }   
}

UserService.java

package com.authc.service;

import java.util.List;

import com.authc.entity.Role;
import com.authc.entity.User;

/**
 * @author lyx
 *
 * 2015-8-26上午9:17:25
 *
 *com.authc.service.Impl.UserService
 *
 */
public interface UserService {

 public List<User> queryAllUserInfo();
 
 public User queryUserByUserName(String username);
 
 public Role queryUserRoleByUsername(String username);
}

5.UserServiceImpl.java

package com.authc.service.Impl;

import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.authc.entity.Permission;
import com.authc.entity.Role;
import com.authc.entity.User;
import com.authc.service.UserService;

public class UserServiceImpl implements UserService {

 @Override
 public List<User> queryAllUserInfo() {

//没有连接数据库,直接在这里添加数据
  Permission perm =new Permission(1,"member:add");
  Role role =new Role();
  role.setRoleName("member");
  role.setPermission(perm);
  User user=new User();
  user.setUsername("liuyuxin");
  user.setPassword("111111");
  user.setRole(role);
 
  Permission perm1 =new Permission(2,"member:delete");
  Role role1 =new Role();
  role1.setRoleName("member");
  role1.setPermission(perm1);
  User user1=new User();
  user1.setUsername("liudong");
  user1.setPassword("111111");
  user1.setRole(role1);
 
  Permission perm2 =new Permission(3,"admin:manage");
  Role role2 =new Role();
  role2.setRoleName("admin");
  role2.setPermission(perm2);
  User user2=new User();
  user2.setUsername("liuhuan");
  user2.setPassword("111111");
  user2.setRole(role2);
 
 
  List<User> userList = new ArrayList<User>();
  userList.add(user);
  userList.add(user1);
  userList.add(user2);
 
  return userList;
 }

 @Override
 public User queryUserByUserName(String username) {
 
  List<User> userList =queryAllUserInfo();
 
  for (User user : userList) {
   
   if(user.getUsername().equals(username))
   {
    return user;
   }
  }
 
  return null;
 }

 @Override
 public Role queryUserRoleByUsername(String username) {
 
 List<User> userList =queryAllUserInfo();
 Role role =new Role();
 
  for (User user : userList) {
   
   if(user.getUsername().equals(username))
   {
    role=user.getRole();
   
    return role;
   }
  }
 
 
  return null;
 }

}

6.UserController.java

package com.authc.controller;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import com.authc.entity.User;
import com.authc.service.UserService;
import com.authc.utils.SpringUtil;
import com.authc.utils.VerifyCodeUtil;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;

/**
 * @author lyx
 *
 * 2015-8-26上午11:56:03
 *
 *com.authc.controller.UserController
 *
 */
@Controller
@RequestMapping("/member")
public class UserController {
 
 //登录
 @RequestMapping("/login")
 public String login()
 {
  return "/login";
 }
 
 /**
  * VerifyCodeUtil.java 类实现验证码
  */
 
 /**
     * 获取验证码图片和文本(验证码文本会保存在HttpSession中)
     */  
    @RequestMapping("/getVerifyCodeImage")  
    public void getVerifyCodeImage(HttpServletRequest request, HttpServletResponse response) throws IOException {  
        //设置页面不缓存  
        response.setHeader("Pragma", "no-cache");  
        response.setHeader("Cache-Control", "no-cache");  
        response.setDateHeader("Expires", 0);  
        String verifyCode = VerifyCodeUtil.generateTextCode(VerifyCodeUtil.TYPE_ALL_MIXED, 4, null);  
        //将验证码放到HttpSession里面  
        request.getSession().setAttribute("verifyCode", verifyCode);  
        System.out.println("本次生成的验证码为[" + verifyCode + "],已存放到HttpSession中");  
        //设置输出的内容的类型为JPEG图像  
        response.setContentType("image/jpeg");  
        BufferedImage bufferedImage = VerifyCodeUtil.generateImageCode(verifyCode, 90, 25, 4, true, Color.WHITE, Color.BLACK, null);  
        //写给浏览器  
        ImageIO.write(bufferedImage, "JPEG", response.getOutputStream());  
    }  
 
 /**
  * @param request
  * @return
  * 登录验证
  */
 @RequestMapping("/toLogin")
 public String toLogin(HttpServletRequest request)
 {
  String username = request.getParameter("username");
  String password = request.getParameter("password");
 
  //返回地址
  String returnUrl="/login";
 
  //获取HttpSession验证码
  String verifyCode =(String) request.getSession().getAttribute("verifyCode");
  //获取用户输入的验证码
  String submitCode = WebUtils.getCleanParam(request,"verifyCode");
 
  System.out.println("用户输入的验证码是:"+submitCode+";系统生成的验证码是:"+verifyCode);
 
  if(StringUtils.isEmpty(submitCode)|| !StringUtils.equalsIgnoreCase(verifyCode, submitCode))
  {
   request.setAttribute("login_msg","验证码错误" );
   return returnUrl;
  }
 
  //根据获取的用户名和密码封装成Token
  UsernamePasswordToken token =new UsernamePasswordToken(username,password);
  //是否记住用户
  token.setRememberMe(true);
  //获取当前的subject
  Subject subject =SecurityUtils.getSubject();
 
  try {
   System.out.println("对用户:["+username+"]进行登录验证,验证开始...");
   //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查  
   //每个Realm都能在必要时对提交的AuthenticationTokens作出反应  
   //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法  
   subject.login(token);
   System.out.println("对用户:["+username+"]进行登录验证,验证通过!");
   returnUrl="/main";
   
  }catch (UnknownAccountException e) {
   
   System.out.println("对用户:["+username+"]进行登录验证,验证未通过!错误:未知账号");
   request.setAttribute("login_msg","未知账号");
   
  }catch (IncorrectCredentialsException e) {
   System.out.println("对用户:["+username+"]进行登录验证,验证未通过!错误:密码错误");
   request.setAttribute("login_msg", "密码错误");
   
  }catch (LockedAccountException e) {
   System.out.println("对用户:["+username+"]进行登录验证,验证未通过!错误:账号被锁定");
   request.setAttribute("login_msg", "账号被锁定");
   
  }catch (ExcessiveAttemptsException e) {
   System.out.println("对用户:["+username+"]进行登录验证,验证未通过!错误:错误次数过多");
   request.setAttribute("login_msg", "密码或用户名输入错误次数过多");
   
  }catch (AuthenticationException e) {
   System.out.println("对用户:["+username+"]进行登录验证,验证未通过!错误:用户名或密码不正确");
   request.setAttribute("login_msg", "身份认证失败,用户名或密码不正确");
  }
 
  //验证是否登录成功
  if(subject.isAuthenticated())
  {
   System.out.println("用户:["+username+"]进行登录验证通过");
  }else
  {
   token.clear();
  }
  return returnUrl;
 }
 
 
 /**
  * @param request
  * @return
  * 注销登录
  */
 
 @RequestMapping("/logout")
 public String logout(HttpServletRequest request)
 {
  SecurityUtils.getSubject().logout();
  return "/login";  
 }
 
 @RequestMapping("/queryMyUserInfo")
 public String queryUserInfo(HttpServletRequest request)
 {
  //从Session中取得当前用户信息
  User currentUser = (User) request.getSession().getAttribute("currentUser");
 
  request.setAttribute("user", currentUser);
 
  return "/myinfo";
 }
}

7. AdminController.java

package com.authc.controller;

import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.authc.entity.User;
import com.authc.service.PermissionService;
import com.authc.service.RoleService;
import com.authc.service.UserService;
import com.authc.service.Impl.UserServiceImpl;

/**
 * @author lyx
 *
 * 2015-8-26上午11:56:45
 *
 *com.authc.controller.AdminController
 *
 */

@Controller
@RequestMapping("/admin")
public class AdminController {
 
 private UserService userService =new UserServiceImpl();
 

 @RequestMapping("/queryAllUserInfo")
 public String queryAllUserInfo(HttpServletRequest request)
 {
  List<User> userList = userService.queryAllUserInfo();
 
  request.setAttribute("userList", userList);

  return "/admin";
 }

}

8. SpringUtil.java

package com.authc.utils;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lyx
 *
 * 2015-8-18下午3:53:19
 *
 *com.utils.SpringUtil
 *	TODO
 */
public class SpringUtil {

 private static ApplicationContext ctx =new ClassPathXmlApplicationContext("springmvc.xml");
 
 
 public static Object getBean(String beanId)
 {
  return ctx.getBean(beanId);
 }
}

VerifyCodeUtil.java 验证码

package com.authc.utils;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 验证码生成器
 * @see --------------------------------------------------------------------------------------------------------------
 * @see 可生成数字、大写、小写字母及三者混合类型的验证码
 * @see 支持自定义验证码字符数量,支持自定义验证码图片的大小,支持自定义需排除的特殊字符,支持自定义干扰线的数量,支持自定义验证码图文颜色
 * @see --------------------------------------------------------------------------------------------------------------
 * @see 另外,给Shiro加入验证码有多种方式,也可以通过继承修改FormAuthenticationFilter类,通过Shiro去验证验证码
 * @see 而这里既然使用了SpringMVC,也为了简化操作,就使用此工具生成验证码,并在Controller中处理验证码的校验
 * @see --------------------------------------------------------------------------------------------------------------
 * @create Sep 29, 2013 4:23:13 PM
 * @author 玄玉<http://blog.csdn.net/jadyer>
 */
public class VerifyCodeUtil {
/**
 * 验证码类型为仅数字,即0~9
 */
public static final int TYPE_NUM_ONLY = 0;

/**
 * 验证码类型为仅字母,即大小写字母混合
 */
public static final int TYPE_LETTER_ONLY = 1;

/**
 * 验证码类型为数字和大小写字母混合
 */
public static final int TYPE_ALL_MIXED = 2;

/**
 * 验证码类型为数字和大写字母混合
 */
public static final int TYPE_NUM_UPPER = 3;

/**
 * 验证码类型为数字和小写字母混合
 */
public static final int TYPE_NUM_LOWER = 4;

/**
 * 验证码类型为仅大写字母
 */
public static final int TYPE_UPPER_ONLY = 5;

/**
 * 验证码类型为仅小写字母
 */
public static final int TYPE_LOWER_ONLY = 6;

private VerifyCodeUtil(){}

/**
 * 生成随机颜色
 */
private static Color generateRandomColor() {
Random random = new Random();
return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
}


/**
 * 生成图片验证码
 * @param type           验证码类型,参见本类的静态属性
 * @param length         验证码字符长度,要求大于0的整数
 * @param excludeString  需排除的特殊字符
 * @param width          图片宽度(注意此宽度若过小,容易造成验证码文本显示不全,如4个字符的文本可使用85到90的宽度)
 * @param height         图片高度
 * @param interLine      图片中干扰线的条数
 * @param randomLocation 每个字符的高低位置是否随机
 * @param backColor      图片颜色,若为null则表示采用随机颜色
 * @param foreColor      字体颜色,若为null则表示采用随机颜色
 * @param lineColor      干扰线颜色,若为null则表示采用随机颜色
 * @return 图片缓存对象
 */
public static BufferedImage generateImageCode(int type, int length, String excludeString, int width, int height, int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor){
String textCode = generateTextCode(type, length, excludeString);
return generateImageCode(textCode, width, height, interLine, randomLocation, backColor, foreColor, lineColor);
}


/**
 * 生成验证码字符串
 * @param type          验证码类型,参见本类的静态属性
 * @param length        验证码长度,要求大于0的整数
 * @param excludeString 需排除的特殊字符(无需排除则为null)
 * @return 验证码字符串
 */
public static String generateTextCode(int type, int length, String excludeString){
if(length <= 0){
return "";
}
StringBuffer verifyCode = new StringBuffer();
int i = 0;
Random random = new Random();
switch(type){
case TYPE_NUM_ONLY:
while(i < length){
int t = random.nextInt(10);
//排除特殊字符
if(null==excludeString || excludeString.indexOf(t+"")<0) {
verifyCode.append(t);
i++;
}
}
break;
case TYPE_LETTER_ONLY:
while(i < length){
int t = random.nextInt(123);
if((t>=97 || (t>=65&&t<=90)) && (null==excludeString||excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
case TYPE_ALL_MIXED:
while(i < length){
int t = random.nextInt(123);
if((t>=97 || (t>=65&&t<=90) || (t>=48&&t<=57)) && (null==excludeString||excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
case TYPE_NUM_UPPER:
while(i < length){
int t = random.nextInt(91);
if((t>=65 || (t>=48&&t<=57)) && (null==excludeString || excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
case TYPE_NUM_LOWER:
while(i < length){
int t = random.nextInt(123);
if((t>=97 || (t>=48&&t<=57)) && (null==excludeString || excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
case TYPE_UPPER_ONLY:
while(i < length){
int t = random.nextInt(91);
if((t >= 65) && (null==excludeString||excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
case TYPE_LOWER_ONLY:
while(i < length){
int t = random.nextInt(123);
if((t>=97) && (null==excludeString||excludeString.indexOf((char)t)<0)){
verifyCode.append((char)t);
i++;
}
}
break;
}
return verifyCode.toString();
}

/**
 * 已有验证码,生成验证码图片
 * @param textCode       文本验证码
 * @param width          图片宽度(注意此宽度若过小,容易造成验证码文本显示不全,如4个字符的文本可使用85到90的宽度)
 * @param height         图片高度
 * @param interLine      图片中干扰线的条数
 * @param randomLocation 每个字符的高低位置是否随机
 * @param backColor      图片颜色,若为null则表示采用随机颜色
 * @param foreColor      字体颜色,若为null则表示采用随机颜色
 * @param lineColor      干扰线颜色,若为null则表示采用随机颜色
 * @return 图片缓存对象
 */
public static BufferedImage generateImageCode(String textCode, int width, int height, int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor){
//创建内存图像
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//获取图形上下文
Graphics graphics = bufferedImage.getGraphics();
//画背景图
graphics.setColor(null==backColor ? generateRandomColor() : backColor);
graphics.fillRect(0, 0, width, height);
//画干扰线
Random random = new Random();
if(interLine > 0){
int x = 0, y = 0, x1 = width, y1 = 0;
for(int i=0; i<interLine; i++){
graphics.setColor(null==lineColor ? generateRandomColor() : lineColor);
y = random.nextInt(height);
y1 = random.nextInt(height);
graphics.drawLine(x, y, x1, y1);
}
}
//字体大小为图片高度的80%
int fsize = (int)(height * 0.8);
int fx = height - fsize;
int fy = fsize;
//设定字体
graphics.setFont(new Font("Default", Font.PLAIN, fsize));
//写验证码字符
for(int i=0; i<textCode.length(); i++){
fy = randomLocation ? (int)((Math.random()*0.3+0.6)*height) : fy;
graphics.setColor(null==foreColor ? generateRandomColor() : foreColor);
//将验证码字符显示到图象中
graphics.drawString(textCode.charAt(i)+"", fx, fy);
fx += fsize * 0.9;
}
graphics.dispose();
return bufferedImage;
}
}

9.login.jsp登录页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page isELIgnored="false"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
request.setAttribute("home", path);
%>
 <!DOCTYPE HTML>
<html lang="en-US"> 
<!-- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> -->
<script type="text/javascript" src="<%=request.getContextPath()%>/res/login/prefixfree.min.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/res/js/jquery-1.11.3.min.js"></script>

<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="<%=request.getContextPath()%>/res/login/login.css" type="text/css"></link>

<script type="text/javascript">
var home ="${home}";
var msg ="${login_msg }";
    
$(function(){  //生成验证码         
    $('#verifyCodeImage').click(function () {  
    $(this).hide().attr('src', '<%=path%>/member/getVerifyCodeImage?' + Math.floor(Math.random()*100) ).fadeIn(); });      
});   
 
window.onbeforeunload = function(){  
    //关闭窗口时自动退出  
    if(event.clientX>360&&event.clientY<0||event.altKey){     
        alert(parent.document.location);  
    }  
};  
             
function changeCode() {  //刷新
    $('#verifyCodeImage').hide().attr('src', '<%=path%>/member/getVerifyCodeImage?' + Math.floor(Math.random()*100) ).fadeIn();  
    event.cancelBubble=true;  
} 

if(msg!="")
{
alert(msg);
}
</script>

</head>
<body>

   <div class="content">
           <form action="<%=request.getContextPath()%>/member/toLogin" method="post" class="login-form">
               <div class="username">
                   <input type="text" name="username" placeholder="emma.watson@gmail.com" autocomplete="on" />
                     <div id="loginMsg"></div>
                    <span class="user-icon icon">u</span>         
               </div>
               <div class="password">
                   <input type="password" name="password" placeholder="*******" />
                  
                   <span class="password-icon icon">p</span>
               </div>
               
               
              <div class="code-div">
                <input type="text" name="verifyCode" placeholder="请输入验证码" />  
        <img id="verifyCodeImage"  src="<%=request.getContextPath()%>/member/getVerifyCodeImage"/> 
      <!--   <a href="javascript:void(0)" οnclick="changeCode()">看不清?换一张</a>  --> 
              </div> 
              	
               <div class="account-control">
                   <input type="checkbox" name="rememberMe" id="Remember me" value="Remember me" checked="checked" />
                   <label for="Remember me" data-on="c" class="check"></label>
                   <label for="Remember me" class="info">Remember me</label>
                  <!--  <input type="hidden" name="rememberMe" value="true"> -->
                   
                   <button type="submit">Login</button>
               </div>

            <p class="not-registered">Not a registered user yet?<a>Sign up now!</a></p>
           
           </form>
          
   </div>
</body>
</html>

10.main.jsp主界面

<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="utf-8"%>
 <%@page isELIgnored="false"%>
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title> 功能界面</title>
 </head>
 <body>
 <ul>
  <li>
  普通用户可访问<a href="<%=request.getContextPath()%>/member/queryMyUserInfo" target="_blank">用户信息页面</a>  
  </li>
  <li>
  管理员可访问<a href="<%=request.getContextPath()%>/admin/queryAllUserInfo" target="_blank">用户列表页面</a>  
   </li>
   <li>
  <a href="<%=request.getContextPath()%>/member/logout" target="_blank">Logout</a>  
  </li>
 </ul>
 </body>
 
</html>

login登陆页面:




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值