Struts2----><s:token />标签防止重复提交

一、防止重复提交原理:
首先
,在页面访问的时候server端产生一个标志位,其保存在session中,同时该标志位放到访问的页面的某个元素中(通常为隐藏域);
其次,在session存在的有效时间内,没有其它操作时其值保持不变,当提交表单到server端时,会判断client端提交过来的标志位和server端的标志位的值是否相等;
最后,判断标志位的值,如果相等,则执行自己期望的操作;如果不相等,则转向指定的页面;

1、JSP使用< s:token/ >标签的时候,Struts2会建立一个UUID(全局唯一的字符串)放在session中,并且会成为一个hidden放在form中。 如

<input type="hidden" name="struts.token" value="DP6WFZ8K88OA7FBT15A7V73VJRZ9KXQA" />

2、token拦截器会判断客户端form提交的token值和session中保存的值是否equals。如果equals则执行Action。否则拦截器直接返回invaid.token结果转向对应的视图,Action对应的方法也不会执行;

二、例子

input.jsp(方法为get时候,刷新浏览器不提示重新发送,可能会重复提交)

  <form action="user" method="get">
    	name:<input name="name">
    	age:<input name="age">
    	<input type="submit" value="add">
    	<s:token></s:token>
    </form><br>

上面的form对应页面生成的源代码(右键,查看源代码)

 <body>
    <form action="user" method="get">
    	name:<input name="name">
    	age:<input name="age">
    	<input type="submit" value="add">
    	<input type="hidden" name="struts.token.name" value="struts.token" />
<input type="hidden" name="struts.token" value="DP6WFZ8K88OA7FBT15A7V73VJRZ9KXQA" />
    </form><br>
  </body>

struts.xml

<struts>
	<constant name="struts.devMode" value="true"></constant>
	<package name="test" namespace="/" extends="struts-default">

		<action name="input" class="com.bjsxt.action.InputAction">
			<result>/input.jsp</result>
		</action>

		<action name="user" class="com.bjsxt.action.UserAction">
			<result>/addOK.jsp</result>
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<interceptor-ref name="token"></interceptor-ref>
			<result name="invalid.token">/error.jsp</result>
		</action>

	</package>


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,对于使用 Spring Boot 和 Shiro 框架的项目,可以参照以下步骤进行搭建: 1. 在 pom.xml 文件中添加依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.18.2</version> </dependency> ``` 其中,shiro-spring-boot-web-starter 是 Shiro 在 Spring Boot 中的集成包,java-jwt 是用于生成和解析 JWT 的库。 2. 在 application.properties 或 application.yml 文件中配置 Shiro: ``` # 配置 Shiro 的加密方式 shiro.hashAlgorithmName=md5 shiro.hashIterations=2 # 配置 JWT 的过期时间 jwt.expireTime=1800 # 配置 Shiro 的登录路径和首页路径 shiro.loginUrl=/login shiro.successUrl=/index # 配置 Shiro 的过滤器链 shiro.filterChainDefinitionMap= \ /static/** = anon \ /login = anon \ /logout = logout \ /** = authc ``` 其中,shiro.hashAlgorithmName 和 shiro.hashIterations 是 Shiro 的加密方式,jwt.expireTime 是 JWT 的过期时间,shiro.loginUrl 和 shiro.successUrl 是 Shiro 的登录路径和首页路径,shiro.filterChainDefinitionMap 是 Shiro 的过滤器链。 3. 创建 Shiro 配置类,用于配置 Shiro 的各种组件: ``` @Configuration public class ShiroConfig { // 配置 SecurityManager @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); // 配置自定义 Realm securityManager.setRememberMeManager(cookieRememberMeManager()); // 配置 RememberMeManager securityManager.setSessionManager(sessionManager()); // 配置 SessionManager return securityManager; } // 配置自定义 Realm @Bean public MyRealm myRealm() { return new MyRealm(); } // 配置 RememberMeManager @Bean public RememberMeManager cookieRememberMeManager() { CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); rememberMeManager.setCookie(rememberMeCookie()); return rememberMeManager; } // 配置 RememberMeCookie @Bean public SimpleCookie rememberMeCookie() { SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setMaxAge(86400); // 设置 Cookie 的过期时间为一天 return cookie; } // 配置 SessionManager @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionIdCookieEnabled(true); sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionDAO(redisSessionDAO()); // 配置 RedisSessionDAO sessionManager.setGlobalSessionTimeout(1800000); // 设置 Session 的过期时间为半小时 sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); return sessionManager; } // 配置 RedisSessionDAO @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); // 配置 RedisManager redisSessionDAO.setKeyPrefix("shiro:session:"); redisSessionDAO.setExpire(1800); // 设置 Session 的过期时间为半小时 return redisSessionDAO; } // 配置 RedisManager @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost("localhost"); redisManager.setPort(6379); redisManager.setTimeout(0); return redisManager; } // 配置 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager()); factoryBean.setLoginUrl("/login"); factoryBean.setSuccessUrl("/index"); factoryBean.setUnauthorizedUrl("/unauthorized"); factoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap()); return factoryBean; } // 配置 ShiroFilterChainDefinition @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); chainDefinition.addPathDefinition("/static/**", "anon"); chainDefinition.addPathDefinition("/login", "anon"); chainDefinition.addPathDefinition("/logout", "logout"); chainDefinition.addPathDefinition("/**", "authc"); return chainDefinition; } } ``` 其中,myRealm() 方法用于配置自定义 Realm,cookieRememberMeManager() 方法用于配置 RememberMeManager 和 RememberMeCookie,sessionManager() 方法用于配置 SessionManager 和 RedisSessionDAO,redisManager() 方法用于配置 RedisManager,shiroFilterFactoryBean() 方法用于配置 ShiroFilterFactoryBean,shiroFilterChainDefinition() 方法用于配置 ShiroFilterChainDefinition。 4. 创建自定义 Realm 类,用于实现用户的认证和授权逻辑: ``` public class MyRealm extends AuthorizingRealm { // 配置加密方式 @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("md5"); matcher.setHashIterations(2); super.setCredentialsMatcher(matcher); } // 用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = new String(upToken.getPassword()); // 根据用户名和密码查询用户信息 User user = userService.findByUsernameAndPassword(username, password); if (user == null) { throw new UnknownAccountException("用户名或密码错误"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); return info; } // 用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); User user = (User) principals.getPrimaryPrincipal(); // 根据用户角色查询权限信息 List<Role> roles = userService.findRolesByUsername(user.getUsername()); for (Role role : roles) { info.addRole(role.getName()); List<Permission> permissions = role.getPermissions(); for (Permission permission : permissions) { info.addStringPermission(permission.getName()); } } return info; } } ``` 在自定义 Realm 类中,通过 setCredentialsMatcher() 方法配置加密方式,通过 doGetAuthenticationInfo() 方法实现用户的认证逻辑,通过 doGetAuthorizationInfo() 方法实现用户的授权逻辑。 5. 创建登录控制器,用于处理用户的登录和注销: ``` @Controller public class LoginController { @GetMapping("/login") public String login() { return "login"; } @PostMapping("/login") public String login(String username, String password, boolean rememberMe, HttpServletRequest request) { try { // 将用户名和密码封装成 UsernamePasswordToken 对象 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 设置 RememberMe token.setRememberMe(rememberMe); // 执行登录操作 SecurityUtils.getSubject().login(token); return "redirect:/index"; } catch (UnknownAccountException e) { request.setAttribute("error", "用户名不存在"); } catch (IncorrectCredentialsException e) { request.setAttribute("error", "密码错误"); } catch (LockedAccountException e) { request.setAttribute("error", "账号已被锁定"); } catch (AuthenticationException e) { request.setAttribute("error", "登录失败:" + e.getMessage()); } return "login"; } @GetMapping("/logout") public String logout() { SecurityUtils.getSubject().logout(); return "redirect:/login"; } } ``` 在登录控制器中,通过 login() 方法实现用户的登录操作,通过 logout() 方法实现用户的注销操作。 6. 在页面中添加登录界面和首页,例如: ``` <!-- 登录界面 --> <form method="post" action="/login"> <div class="form-group"> <label for="username">用户名:</label> <input type="text" id="username" name="username" class="form-control" required> </div> <div class="form-group"> <label for="password">密码:</label> <input type="password" id="password" name="password" class="form-control" required> </div> <div class="form-check"> <input type="checkbox" id="rememberMe" name="rememberMe" class="form-check-input"> <label for="rememberMe" class="form-check-label">记住我</label> </div> <button type="submit" class="btn btn-primary">登录</button> </form> <!-- 首页 --> <h1>欢迎登录</h1> <p><a href="/logout">退出登录</a></p> ``` 通过以上步骤,就可以使用 Spring Boot 和 Shiro 框架来实现用户的认证和授权了,当然,在具体实现时,还需要针对自己的业务需求进行一些调整和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值