4 |
5 |
--------------------------------------------------------------------------------
/src/test/java/com/jadyer/demo/test/JettyBootStrap.java:
--------------------------------------------------------------------------------
1 | package com.jadyer.demo.test;
2 |
3 | import org.eclipse.jetty.server.Connector;
4 | import org.eclipse.jetty.server.Server;
5 | import org.eclipse.jetty.server.nio.SelectChannelConnector;
6 | import org.eclipse.jetty.webapp.WebAppContext;
7 |
8 | /**
9 | * 完整版见https://github.com/jadyer/JadyerEngine/blob/master/JadyerEngine-web/src/test/java/com/jadyer/engine/JettyBootStrap.java
10 | */
11 | public class JettyBootStrap {
12 | private static int port = 80;
13 | private static String contextPath = "/";
14 |
15 | public static void main(String[] args) {
16 | long beginTime = System.currentTimeMillis();
17 | Server server = createServer();
18 | try {
19 | server.start();
20 | System.err.println();
21 | System.out.println("*****************************************************************");
22 | System.err.print("[INFO] Server running in " + (System.currentTimeMillis() - beginTime) + "ms ");
23 | System.err.println("at http://127.0.0.1" + (80==port?"":":"+port) + contextPath);
24 | System.out.println("*****************************************************************");
25 | } catch (Exception e) {
26 | System.err.println("Jetty启动失败,堆栈轨迹如下");
27 | e.printStackTrace();
28 | System.exit(-1);
29 | }
30 | }
31 |
32 | @SuppressWarnings("ConstantConditions")
33 | private static Server createServer(){
34 | Server server = new Server();
35 | server.setStopAtShutdown(true);
36 | SelectChannelConnector connector = new SelectChannelConnector();
37 | connector.setPort(port);
38 | connector.setReuseAddress(false);
39 | server.setConnectors(new Connector[]{connector});
40 | String webAppPath = JettyBootStrap.class.getClassLoader().getResource(".").getFile();
41 | webAppPath = webAppPath.substring(0, webAppPath.indexOf("target")) + "src/main/webapp";
42 | WebAppContext context = new WebAppContext(webAppPath, contextPath);
43 | server.setHandler(context);
44 | return server;
45 | }
46 | }
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.jadyer.demo
6 | demo-springmvc-shiro
7 | 1.1
8 | war
9 |
10 |
11 | UTF-8
12 |
13 |
14 |
15 |
16 | org.eclipse.jetty
17 | jetty-server
18 | 8.1.17.v20150415
19 | provided
20 |
21 |
22 | org.eclipse.jetty
23 | jetty-webapp
24 | 8.1.17.v20150415
25 | provided
26 |
27 |
28 | org.eclipse.jetty
29 | jetty-jsp
30 | 8.1.17.v20150415
31 | provided
32 |
33 |
34 | org.springframework
35 | spring-webmvc
36 | 3.2.6.RELEASE
37 |
38 |
39 | org.apache.commons
40 | commons-lang3
41 | 3.4
42 |
43 |
44 | org.apache.shiro
45 | shiro-spring
46 | 1.2.2
47 |
48 |
49 |
50 |
51 |
52 |
53 | org.apache.maven.plugins
54 | maven-compiler-plugin
55 | 3.5.1
56 |
57 | 1.7
58 | 1.7
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | contextConfigLocation
9 | classpath:applicationContext.xml
10 |
11 |
12 |
13 | org.springframework.web.context.ContextLoaderListener
14 |
15 |
16 |
17 | SpringMVC
18 | org.springframework.web.servlet.DispatcherServlet
19 |
20 | contextConfigLocation
21 | classpath:applicationContext.xml
22 |
23 |
24 |
25 | SpringMVC
26 | /
27 |
28 |
29 |
35 |
36 | shiroFilter
37 | org.springframework.web.filter.DelegatingFilterProxy
38 |
39 |
40 | targetFilterLifecycle
41 | true
42 |
43 |
44 |
45 | shiroFilter
46 | /*
47 |
48 |
49 |
50 |
51 | 45
52 |
53 |
--------------------------------------------------------------------------------
/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
47 |
48 |
56 |
57 |
58 | /main** = authc
59 | /admin/list** = authc,perms[admin:manage]
60 | /user/info-anon** = anon
61 | /user/info** = authc
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
76 |
82 |
--------------------------------------------------------------------------------
/src/main/java/com/jadyer/demo/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.jadyer.demo.controller;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
5 | import org.apache.commons.lang3.builder.ToStringStyle;
6 | import org.apache.shiro.SecurityUtils;
7 | import org.apache.shiro.authc.AuthenticationException;
8 | import org.apache.shiro.authc.ExcessiveAttemptsException;
9 | import org.apache.shiro.authc.IncorrectCredentialsException;
10 | import org.apache.shiro.authc.LockedAccountException;
11 | import org.apache.shiro.authc.UnknownAccountException;
12 | import org.apache.shiro.authc.UsernamePasswordToken;
13 | import org.apache.shiro.subject.Subject;
14 | import org.apache.shiro.web.util.WebUtils;
15 | import org.springframework.stereotype.Controller;
16 | import org.springframework.web.bind.annotation.RequestMapping;
17 | import org.springframework.web.bind.annotation.RequestMethod;
18 | import org.springframework.web.servlet.view.InternalResourceViewResolver;
19 |
20 | import javax.servlet.http.HttpServletRequest;
21 | import javax.servlet.http.HttpSession;
22 |
23 | /**
24 | * SpringMVC-3.2.4整合Shiro-1.2.2
25 | * Created by 玄玉 on 2013/09/30 23:37.
26 | */
27 | @Controller
28 | @RequestMapping("mydemo")
29 | public class UserController {
30 | @RequestMapping("/logout")
31 | public String logout(HttpSession session){
32 | String currentUser = (String)session.getAttribute("currentUser");
33 | System.out.println("用户[" + currentUser + "]准备登出");
34 | SecurityUtils.getSubject().logout();
35 | System.out.println("用户[" + currentUser + "]已登出");
36 | return InternalResourceViewResolver.REDIRECT_URL_PREFIX + "/";
37 | }
38 |
39 | @RequestMapping(value="/login", method=RequestMethod.POST)
40 | public String login(String username, String password, HttpServletRequest request){
41 | System.out.println("-------------------------------------------------------");
42 | String rand = (String)request.getSession().getAttribute("rand");
43 | String captcha = WebUtils.getCleanParam(request, "captcha");
44 | System.out.println("用户["+username+"]登录时输入的验证码为["+captcha+"],HttpSession中的验证码为["+rand+"]");
45 | if(!StringUtils.equals(rand, captcha)){
46 | request.setAttribute("message_login", "验证码不正确");
47 | return InternalResourceViewResolver.FORWARD_URL_PREFIX + "/";
48 | }
49 | UsernamePasswordToken token = new UsernamePasswordToken(username, password);
50 | token.setRememberMe(true);
51 | System.out.print("为验证登录用户而封装的Token:");
52 | System.out.println(ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
53 | //获取当前的Subject
54 | Subject currentUser = SecurityUtils.getSubject();
55 | try {
56 | //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
57 | //每个Realm都能在必要时对提交的AuthenticationTokens作出反应
58 | //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法
59 | System.out.println("对用户[" + username + "]进行登录验证...验证开始");
60 | currentUser.login(token);
61 | System.out.println("对用户[" + username + "]进行登录验证...验证通过");
62 | }catch(UnknownAccountException uae){
63 | System.out.println("对用户[" + username + "]进行登录验证...验证未通过,未知账户");
64 | request.setAttribute("message_login", "未知账户");
65 | }catch(IncorrectCredentialsException ice){
66 | System.out.println("对用户[" + username + "]进行登录验证...验证未通过,错误的凭证");
67 | request.setAttribute("message_login", "密码不正确");
68 | }catch(LockedAccountException lae){
69 | System.out.println("对用户[" + username + "]进行登录验证...验证未通过,账户已锁定");
70 | request.setAttribute("message_login", "账户已锁定");
71 | }catch(ExcessiveAttemptsException eae){
72 | System.out.println("对用户[" + username + "]进行登录验证...验证未通过,错误次数过多");
73 | request.setAttribute("message_login", "用户名或密码错误次数过多");
74 | }catch(AuthenticationException ae){
75 | //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
76 | System.out.println("对用户[" + username + "]进行登录验证...验证未通过,堆栈轨迹如下");
77 | ae.printStackTrace();
78 | request.setAttribute("message_login", "用户名或密码不正确");
79 | }
80 | //验证是否登录成功
81 | if(currentUser.isAuthenticated()){
82 | System.out.println("用户[" + username + "]登录认证通过(这里可进行一些认证通过后的系统参数初始化操作)");
83 | return "main";
84 | }else{
85 | token.clear();
86 | return InternalResourceViewResolver.FORWARD_URL_PREFIX + "/";
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/main/java/com/jadyer/demo/realm/MyRealm.java:
--------------------------------------------------------------------------------
1 | package com.jadyer.demo.realm;
2 |
3 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
4 | import org.apache.commons.lang3.builder.ToStringStyle;
5 | import org.apache.shiro.SecurityUtils;
6 | import org.apache.shiro.authc.AuthenticationException;
7 | import org.apache.shiro.authc.AuthenticationInfo;
8 | import org.apache.shiro.authc.AuthenticationToken;
9 | import org.apache.shiro.authc.SimpleAuthenticationInfo;
10 | import org.apache.shiro.authc.UsernamePasswordToken;
11 | import org.apache.shiro.authz.AuthorizationInfo;
12 | import org.apache.shiro.authz.SimpleAuthorizationInfo;
13 | import org.apache.shiro.realm.AuthorizingRealm;
14 | import org.apache.shiro.session.Session;
15 | import org.apache.shiro.subject.PrincipalCollection;
16 | import org.apache.shiro.subject.Subject;
17 |
18 | /**
19 | * 自定义的指定Shiro验证用户登录的类
20 | * 这里定义了两个用户:jadyer(拥有admin角色和admin:manage权限)、xuanyu(无任何角色和权限)
21 | * Created by 玄玉 on 2013/09/29 15:15.
22 | */
23 | public class MyRealm extends AuthorizingRealm {
24 | /**
25 | * 为当前登录的Subject授予角色和权限
26 | * -----------------------------------------------------------------------------------------------
27 | * 经测试:本例中该方法的调用时机为需授权资源被访问时
28 | * 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache
29 | * 个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache
30 | * 比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache
31 | * -----------------------------------------------------------------------------------------------
32 | */
33 | @Override
34 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){
35 | //获取当前登录的用户名
36 | String currentUsername = (String)super.getAvailablePrincipal(principals);
37 | ////从数据库中获取当前登录用户的详细信息
38 | //List roleList = new ArrayList();
39 | //List permissionList = new ArrayList();
40 | //User user = userService.getByUsername(currentUsername);
41 | //if(null != user){
42 | // //实体类User中包含有用户角色的实体类信息
43 | // if(null!=user.getRoles() && user.getRoles().size()>0){
44 | // //获取当前登录用户的角色
45 | // for(Role role : user.getRoles()){
46 | // roleList.add(role.getName());
47 | // //实体类Role中包含有角色权限的实体类信息
48 | // if(null!=role.getPermissions() && role.getPermissions().size()>0){
49 | // //获取权限
50 | // for(Permission pmss : role.getPermissions()){
51 | // if(StringUtils.isNotBlank(pmss.getPermission())){
52 | // permissionList.add(pmss.getPermission());
53 | // }
54 | // }
55 | // }
56 | // }
57 | // }
58 | //}else{
59 | // throw new AuthorizationException();
60 | //}
61 | ////为当前用户设置角色和权限
62 | //SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
63 | //simpleAuthorInfo.addRoles(roleList);
64 | //simpleAuthorInfo.addStringPermissions(permissionList);
65 | //实际中可能会像上面注释的那样,从数据库或缓存中取得用户的角色和权限信息
66 | SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
67 | if(null!=currentUsername && "jadyer".equals(currentUsername)){
68 | //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
69 | simpleAuthorInfo.addRole("admin");
70 | //添加权限
71 | simpleAuthorInfo.addStringPermission("admin:manage");
72 | System.out.println("已为用户[jadyer]赋予了[admin]角色和[admin:manage]权限");
73 | return simpleAuthorInfo;
74 | }
75 | if(null!=currentUsername && "xuanyu".equals(currentUsername)){
76 | System.out.println("当前用户[xuanyu]无授权(不需要为其赋予角色和权限)");
77 | return simpleAuthorInfo;
78 | }
79 | //若该方法什么都不做直接返回null的话
80 | //就会导致任何用户访问/admin/listUser.jsp时都会自动跳转到unauthorizedUrl指定的地址
81 | //详见applicationContext.xml中的的配置
82 | return null;
83 | }
84 |
85 | /**
86 | * 验证当前登录的Subject
87 | * 经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()的时候
88 | */
89 | @Override
90 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
91 | //获取基于用户名和密码的令牌
92 | //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
93 | //两个token的引用都是一样的,本例中是:org.apache.shiro.authc.UsernamePasswordToken@33799a1e
94 | UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
95 | System.out.print("验证当前Subject时获取到token:");
96 | System.out.println(ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
97 | //User user = userService.getByUsername(token.getUsername());
98 | //if(null != user){
99 | // String username = user.getUsername();
100 | // String password = user.getPassword();
101 | // String nickname = user.getNickname();
102 | // AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(username, password, nickname);
103 | // this.setSession("currentUser", user);
104 | // return authcInfo;
105 | //}else{
106 | // return null;
107 | //}
108 | //此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
109 | //说白了就是第一个参数填登录用户名,第二个参数填合法的登录密码(可以是从数据库中取到的,本例中为了演示就硬编码了)
110 | //这样一来,在随后的登录页面上就只有这里指定的用户和密码才能通过验证
111 | if("jadyer".equals(token.getUsername())){
112 | AuthenticationInfo authcInfo = new SimpleAuthenticationInfo("jadyer", "jadyer", this.getName());
113 | this.setAuthenticationSession("jadyer");
114 | return authcInfo;
115 | }
116 | if("xuanyu".equals(token.getUsername())){
117 | AuthenticationInfo authcInfo = new SimpleAuthenticationInfo("xuanyu", "xuanyu", this.getName());
118 | this.setAuthenticationSession("xuanyu");
119 | return authcInfo;
120 | }
121 | //没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常
122 | return null;
123 | }
124 |
125 | /**
126 | * 将一些数据放到ShiroSession中,以便于其它地方使用
127 | * 比如Controller里面,使用时直接用HttpSession.getAttribute(key)就可以取到
128 | */
129 | private void setAuthenticationSession(Object value){
130 | Subject currentUser = SecurityUtils.getSubject();
131 | if(null != currentUser){
132 | Session session = currentUser.getSession();
133 | System.out.println("当前Session超时时间为[" + session.getTimeout() + "]毫秒");
134 | session.setTimeout(1000 * 60 * 60 * 2);
135 | System.out.println("修改Session超时时间为[" + session.getTimeout() + "]毫秒");
136 | session.setAttribute("currentUser", value);
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/src/main/webapp/captcha.jsp:
--------------------------------------------------------------------------------
1 | <%@ page contentType="image/jpeg; charset=UTF-8" pageEncoding="UTF-8"%>
2 | <%@ page import="java.awt.Color"%>
3 | <%@ page import="java.util.Random"%>
4 | <%@ page import="java.awt.image.BufferedImage"%>
5 | <%@ page import="java.awt.Graphics"%>
6 | <%@ page import="java.awt.Font"%>
7 | <%@ page import="javax.imageio.ImageIO"%>
8 |
9 | <%--
10 | 这是一个用于生成随机验证码图片的JSP文件
11 | 这里contentType="image/jpeg"用来告诉容器:该JSP文件的输出格式为图片格式
12 | 登录网站时,通常要求输入随机生成的验证码,这是为了防止有些软件会自动生成破解密码
13 | 这些验证码一般都是通过图片显示出来的,并且图片上有很多不规则的线条或者图案来干扰,使得软件不容易识别图案上的验证码
14 | --%>
15 |
16 | <%!
17 | /**
18 | * 定义验证码类型(1--纯数字,2--纯汉字)
19 | * 这里也支持数字和英文字母组合,但考虑到不好辨认,故注释了这部分代码,详见第69行
20 | */
21 | int captchaType = 1;
22 |
23 | /**
24 | * 生成给定范围内的随机颜色
25 | */
26 | Color getRandColor(Random random, int fc, int bc){
27 | if(fc>255) fc = 255;
28 | if(bc>255) bc = 255;
29 | int r = fc + random.nextInt(bc-fc);
30 | int g = fc + random.nextInt(bc-fc);
31 | int b = fc + random.nextInt(bc-fc);
32 | return new Color(r, g, b);
33 | }
34 | %>
35 |
36 | <%
37 | //设置页面不缓存
38 | response.setHeader("Pragma", "No-cache");
39 | response.setHeader("Cache-Control", "no-cache");
40 | response.setDateHeader("Expires", 0);
41 |
42 | //创建随机类实例
43 | Random random = new Random();
44 | //定义图片尺寸
45 | int width=60*this.captchaType, height=(this.captchaType==1)?20:30;
46 | //创建内存图像
47 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
48 | //获取图形上下文
49 | Graphics g = image.getGraphics();
50 | //设定背景色
51 | g.setColor(this.getRandColor(random, 200, 250));
52 | //设定图形的矩形坐标及尺寸
53 | g.fillRect(0, 0, width, height);
54 |
55 | String sRand = "";
56 | if(this.captchaType == 1){
57 | //图片背景随机产生50条干扰线作为噪点
58 | g.setColor(this.getRandColor(random, 160, 200));
59 | g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
60 | for(int i=0; i<50; i++){
61 | int x11 = random.nextInt(width);
62 | int y11 = random.nextInt(height);
63 | int x22 = random.nextInt(width);
64 | int y22 = random.nextInt(height);
65 | g.drawLine(x11, y11, x11+x22, y11+y22);
66 | }
67 | //取随机产生的4个数字作为验证码
68 | //String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
69 | //String str = "abcdefghkmnpqrstwxyABCDEFGHJKLMNPRSTWXYZ123456789";
70 | for(int i=0; i<4; i++){
71 | //String rand = String.valueOf(str.charAt(random.nextInt(62)));
72 | //String rand = String.valueOf(str.charAt(random.nextInt(49)));
73 | String rand = String.valueOf(random.nextInt(10));
74 | sRand += rand;
75 | g.setColor(this.getRandColor(random, 10, 150));
76 | //将此数字画到图片上
77 | g.drawString(rand, 13*i+6, 16);
78 | }
79 | }else{
80 | //设定备选汉字
81 | String base = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740" +
82 | "\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b" +
83 | "\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1" +
84 | "\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001" +
85 | "\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1" +
86 | "\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1" +
87 | "\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0" +
88 | "\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee" +
89 | "\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168" +
90 | "\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d" +
91 | "\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c" +
92 | "\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0" +
93 | "\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f" +
94 | "\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf" +
95 | "\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597" +
96 | "\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3" +
97 | "\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be" +
98 | "\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173" +
99 | "\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757" +
100 | "\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752" +
101 | "\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6" +
102 | "\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531" +
103 | "\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5" +
104 | "\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4" +
105 | "\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c" +
106 | "\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a" +
107 | "\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834" +
108 | "\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce" +
109 | "\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559" +
110 | "\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645" +
111 | "\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165" +
112 | "\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c" +
113 | "\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
114 | //图片背景增加噪点
115 | g.setColor(this.getRandColor(random, 160, 200));
116 | g.setFont(new Font("Times New Roman", Font.PLAIN, 14));
117 | for(int i=0; i<6; i++){
118 | g.drawString("*********************************************", 0, 5*(i+2));
119 | }
120 | //设定验证码汉字的备选字体{"宋体", "新宋体", "黑体", "楷体", "隶书"}
121 | String[] fontTypes = {"\u5b8b\u4f53", "\u65b0\u5b8b\u4f53", "\u9ed1\u4f53", "\u6977\u4f53", "\u96b6\u4e66"};
122 | //取随机产生的4个汉字作为验证码
123 | for(int i=0; i<4; i++){
124 | int start = random.nextInt(base.length());
125 | String rand = base.substring(start, start+1);
126 | sRand += rand;
127 | g.setColor(this.getRandColor(random, 10, 150));
128 | g.setFont(new Font(fontTypes[random.nextInt(fontTypes.length)], Font.BOLD, 18+random.nextInt(4)));
129 | //将此汉字画到图片上
130 | g.drawString(rand, 24*i+10+random.nextInt(8), 24);
131 | }
132 | }
133 |
134 | //将验证码存入SESSION
135 | session.setAttribute("rand", sRand);
136 |
137 | //图像生效
138 | g.dispose();
139 | //输出图像到页面
140 | ImageIO.write(image, "PNG", response.getOutputStream());
141 |
142 | //若无下面两行代码,则每次请求生成验证码图片时
143 | //尽管不会影响到图片的生成以及验证码的校验,但控制台都会滚动下面的异常
144 | //java.lang.IllegalStateException: getOutputStream() has already been called for this response
145 | out.clear();
146 | out = pageContext.pushBody();
147 | %>
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------