shiro延伸
spring-异常处理
关于异常处理有四种方法:
第一种:使用SimpleMappingExceptionResolver解析器
在mvc的配置文件中配置异常处理解析器
1 异常处理解析器 2 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 3 <!--默认的错误视图--> 4 <property name="defaultErrorView" value="error"/> 5 <!--异常属性--> 6 <property name="exceptionAttribute" value="ex"> 7 <!--<props>--> 8 <!--<prop key="异常类型1">--> 9 <!--error1--> 10 <!--</prop>--> 11 <!--<prop key="异常类型2">--> 12 <!--error2--> 13 <!--</prop>--> 14 <!--</props>--> 15 </property> 16 </bean>
第二种:
新建一个BaseController类,并在方法前使用@ExceptionHandler注解
1 package com.aaa.controller; 2 3 /** 4 * @Author 刘其佳 5 * 2019/8/14 -- 10:56 6 * @Version 1.0 7 */ 8 9 import org.apache.shiro.authc.AuthenticationException; 10 import org.apache.shiro.authz.AuthorizationException; 11 import org.springframework.ui.Model; 12 import org.springframework.web.bind.annotation.ExceptionHandler; 13 14 /** 15 * 异常处理的第二种方法 16 */ 17 public class BaseController { 18 @ExceptionHandler(RuntimeException.class) 19 public String handler1(RuntimeException ex, Model model){ 20 if(ex instanceof AuthenticationException){ 21 System.out.println("处理方案1"); 22 model.addAttribute("ex",ex); 23 return "error"; 24 }else if(ex instanceof AuthorizationException){ 25 System.out.println("处理方案2"); 26 model.addAttribute("ex",ex); 27 return "error"; 28 } 29 return "error"; 30 } 31 }
控制器继承BaseController
1 package com.aaa.controller; 2 3 import com.aaa.service.UserService; 4 import org.apache.shiro.SecurityUtils; 5 import org.apache.shiro.authc.*; 6 import org.apache.shiro.authz.annotation.RequiresPermissions; 7 import org.apache.shiro.subject.Subject; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Controller; 12 import org.springframework.web.bind.annotation.*; 13 14 import java.util.List; 15 16 /** 17 * @Author 刘其佳 18 * 2019/8/12 -- 16:43 19 * @Version 1.0 20 */ 21 @Controller 22 //@RequestMapping("/user") 23 public class UserController extends BaseController{ 24 public class UserController{ 25 @Autowired 26 private UserService userService; 27 //获取日志对象 28 private static final transient Logger log = LoggerFactory.getLogger(UserController.class); 29 30 @RequestMapping("/login") 31 public String login(String username,String password){ 32 //创建subject 33 Subject currentUser= SecurityUtils.getSubject(); 34 // currentUser.isAuthenticated()当前用户是否被认证 35 // 如果当前用户被认证了,说明用户还是处于登录状态 36 if (!currentUser.isAuthenticated()) { 37 //用户名密码令牌:将表单中的用户名和密码封装进token中 38 UsernamePasswordToken token = new UsernamePasswordToken(username, password); 39 //记住我 40 // token.setRememberMe(true); 41 try { 42 // 调用subject中的login方法来进行匹配用户是否可以登录成功 43 // login方法的参数需要接收shiro的UsernamePasswordToken类型 44 currentUser.login(token);//找到xml文件中id=shiroFilter对应的realm 45 } catch (UnknownAccountException uae) {//账号不存在 46 log.info("There is no user with username of " + token.getPrincipal()); 47 throw new UnknownAccountException("账号不存在"); 48 // return "login"; 49 } catch (IncorrectCredentialsException ice) {//密码错误 50 log.info("Password for account " + token.getPrincipal() + " was incorrect!"); 51 throw new IncorrectCredentialsException("密码错误"); 52 // return "login"; 53 } catch (LockedAccountException lae) {//账号锁死 54 log.info("The account for username " + token.getPrincipal() + " is locked. " + 55 "Please contact your administrator to unlock it."); 56 } 57 catch (AuthenticationException ae) { 58 } 59 } 60 return "front/ok"; 61 } 62 63 @GetMapping("/delete/{id}") 64 @ResponseBody 65 @RequiresPermissions("emp/delete") 66 public boolean delete(@PathVariable("id") Integer id){ 67 int result=userService.deleteByPrimaryKey(id); 68 return result>0?true:false; 69 } 70 }
第三种:使用 @ControllerAdvice+ @ ExceptionHandler 注解 (推荐使用)
同步方式:
1 package com.aaa.controller; 2 3 import com.aaa.entity.Result; 4 import org.apache.shiro.authc.AuthenticationException; 5 import org.apache.shiro.authz.AuthorizationException; 6 import org.springframework.ui.Model; 7 import org.springframework.web.bind.annotation.ControllerAdvice; 8 import org.springframework.web.bind.annotation.ExceptionHandler; 9 import org.springframework.web.bind.annotation.ResponseBody; 10 11 /** 12 * @Author 刘其佳 13 * 2019/8/14 -- 11:08 14 * @Version 1.0 15 */ 16 17 //处理异常的第三种方法之同步方式 18 @ControllerAdvice 19 public class ExceptionController { 20 @ExceptionHandler(RuntimeException.class) 21 public String handler1(RuntimeException ex, Model model){ 22 if(ex instanceof AuthenticationException){ 23 System.out.println("处理方案1"); 24 model.addAttribute("ex",ex); 25 return "error"; 26 }else if(ex instanceof AuthorizationException){ 27 System.out.println("处理方案2"); 28 model.addAttribute("ex",ex); 29 return "error"; 30 } 31 return "error"; 32 }
异步方式:返回的是json数据
步骤:1、自定义实体类型(状态码,错误信息)
2、处理异常时返回该类型的数据
实体类:
1 package com.aaa.entity; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 import lombok.NoArgsConstructor; 6 import lombok.ToString; 7 8 /** 9 * @Author 刘其佳 10 * 2019/8/14 -- 11:12 11 * @Version 1.0 12 */ 13 @Data 14 @AllArgsConstructor 15 @NoArgsConstructor 16 @ToString 17 public class Result { 18 private Integer statusCode; 19 private String message; 20 }
此处说明一下,该实体类使用了lombok的注解:
首先导入Lombok的jar包:
1 <!--lombok:简化实体类的编写--> 2 <dependency> 3 <groupId>org.projectlombok</groupId> 4 <artifactId>lombok</artifactId> 5 <version>1.18.8</version> 6 </dependency>
然后在setting中的plugins下载Lombok的插件
异常处理:
1 //处理异常的第三种方法之异步方式 2 @ExceptionHandler(RuntimeException.class) 3 @ResponseBody 4 public Result handler1(RuntimeException ex){ 5 Result result=new Result(); 6 if(ex instanceof AuthenticationException){ 7 result.setStatusCode(501); 8 result.setMessage("认证异常:"+ex.getMessage()); 9 System.out.println("处理方案1"); 10 }else if(ex instanceof AuthorizationException){ 11 result.setStatusCode(502); 12 result.setMessage("授权异常:"+ex.getMessage()); 13 System.out.println("处理方案2"); 14 } 15 return result; 16 }
第四种方法:
实现HandlerExceptionResolver接口
定时任务
借助Scheduled来实现定时任务
1、配置注解
14 <!--启用定时任务--> 5 <task:annotation-driven/>
2、创建定时任务
1 package com.aaa.controller; 2 3 import org.springframework.scheduling.annotation.Scheduled; 4 import org.springframework.stereotype.Controller; 5 6 /** 7 * @Author 刘其佳 8 * 2019/8/14 -- 17:00 9 * @Version 1.0 10 */ 11 12 @Controller 13 public class SchController {
//每隔5秒执行一次 14 @Scheduled(cron = "0/5 * * * * ?") 15 public static void task() { 16 System.out.println("每一个不曾起舞的日子,都是对生命的辜负"); 17 } 18 }
对于Scheduled属性的说明:
字段 | 允许值 | 允许的特殊符号 |
---|---|---|
秒 | 0-59 | ,-*/ |
分 | 0-59 | ,-*/ |
小时 | 0-23 | ,-*/ |
日期 | 1-31 | ,-*?/LWC |
月份 | 1-12 | ,-*/ |
星期 | 0-7或SUN-SAT 0 7是SUN | ,-*?/LC# |
特殊符号的释义如下:
特殊符号 | 代表含义 |
---|---|
, | 枚举 |
- | 区间 |
* | 任意 |
/ | 步长 |
? | 日/星期冲突匹配 |
L | 最后 |
W | 工作日 |
C | 和Calendar联系后计算过的值 |
# | 星期 4#2 第2个星期四 |
示例:
0 0 0 * * * -- 每天零时执行一次
0 0/15 14,18 * * ? -- 每天14点整和18点整,每隔15分钟执行一次
0 15 10 ? * 1-6 -- 每个月的周一到周六 10:15分执行一次
0 0 2 ? * 6L -- 每个月的最后一个周六凌晨2点执行一次
0 0 2 LW * ? -- 每个月的最后一个工作日凌晨2点执行一次
0 0 2-4 ? * 1#1 -- 每个月的第一个周一凌晨2点到4点期间,每个整点都执行一次
补充注解:
关于@ResponseBody和@RequestBody的区别:
正常情况下,在控制成的方法一般返回字符串,然后根据配置文件中相关配置,为返回的字符串加上响应的前缀和后缀,组成一个路径;
但是,当在方法前加上@responseBody,返回的是json格式的数据,并不能跳转。即@ResponseBody:将数据转换成json并输出到响应流中
@ResponseBody:
1 @GetMapping("/{empno}") 2 @ResponseBody 3 public Emp queryById( @PathVariable("empno") Integer empno){ 4 return empService.selectByPrimaryKey(empno); 5 }
而@ResponseBody:将请求中的json数据转换成Java对象,一般用来处理复杂类型的数据;
@ResponseBody:
1 // requestBody测试 2 function test1() { 3 //数组 4 var emps=[]; 5 emps.push({job:"经理1",hiredate:"2018-1-1"}); 6 emps.push({job:"经理2",hiredate:"2018-1-2"}); 7 emps.push({job:"经理3",hiredate:"2018-1-3"}); 8 $.ajax({ 9 type:"post", 10 url:"${pageContext.request.contextPath}/emp/test1", 11 data:JSON.stringify(emps),//转成json的字符串传过去 12 contentType:'application/json',//内容的类型是:json数据 13 success:function (data) { 14 alert(data); 15 }, 16 error:function (msg) { 17 alert('test1函数发生错误:'+msg); 18 } 19 }) 20 }
1 //在控制层的方法中: 2 @RequestMapping("/test1") 3 @ResponseBody 4 public String test1(@RequestBody List<Emp> emps){//借用@RequestBody将传过来的json转换成Emp对象类型 5 System.out.println(emps); 6 return "ok"; 7 }
在进行@RequestBody的测试中,Ajax中将一个Emp对象转换成json格式,再作为参数传递:
注意书写:1、JSON.stringify(emps):转换成json格式的字符串类型
2、contentType:'application/json':声明一下内容的类型为json格式数据
shiro--rememberMe
在shiro中有一个属性为setRememberMe();即将当前登录用户的信息保存进cookie中;然后可以在shiro的配置文件中指定
如果将信息保存进cookie中了,关闭浏览器再次打开次地址,可以在不登录的情况下访问定义为user的路径(关闭浏览器不将cookie清空),
但是如果注销了当前用户的话(在shiro的配置文件里可以定义某路径的属性为logout,即为注销),那么会讲cookie清空。
先在shiro的配置文件中配置cookie
<!--+++++++++++++++++ 配置cookies -++++++++++++++++++--> <bean id="rememberCookies" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"></constructor-arg> <property name="httpOnly" value="true"></property> <property name="maxAge" value="#{60*60*24}"></property> </bean> <!--++++++++++++++++配置记住我管理器+++++++++++++++++++++--> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('6ZmI6I2j5Y+R5aSn5ZOlAA==')}"/> <property name="cookie" ref="rememberCookies"/> </bean>
在shiro中的安全管理器中进行声明
1 <!--+++++++++++++++++++安全管理器+++++++++++++++--> 2 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 3 <!--缓存管理器--> 4 <property name="cacheManager" ref="cacheManager"/> 5 6 <!--会话的模式;native:本地--> 7 <property name="sessionMode" value="native"/> 8 9 <!--配置realm--> 10 <property name="realm" ref="myRealm"/> 11 12 <!--rememberMe管理器--> 13 <property name="rememberMeManager" ref="rememberMeManager"/> 14 </bean>
定义路径
1 <!--filterChainDefinitions:过滤器的规则声明--> 2 <property name="filterChainDefinitions"> 3 <value> 4 <!--对某些路径进行何种方式的验证,存在先后顺序,不会覆盖,有重复声明的话按照先声明的来--> 5 <!--anon:匿名过滤器,不需要登录 authc:需要进行认证登录 6 uesr:使用了rememberMe后可以直接进入此路径--> 7 /index.jsp = anon 8 /emp/delete = anon 9 /logout=logout 10 /*.jar = anon 11 /common/**=anon 12 /static/** =anon 13 /emp/**=authc 14 /admin/**=authc 15 /user1/**=user 16 </value> 17 </property>
1 package com.aaa.controller; 2 3 import com.aaa.service.UserService; 4 import org.apache.shiro.SecurityUtils; 5 import org.apache.shiro.authc.*; 6 import org.apache.shiro.authz.annotation.RequiresPermissions; 7 import org.apache.shiro.subject.Subject; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Controller; 12 import org.springframework.web.bind.annotation.*; 13 14 import java.util.List; 15 16 /** 17 * @Author 刘其佳 18 * 2019/8/12 -- 16:43 19 * @Version 1.0 20 */ 21 @Controller 22 //@RequestMapping("/user") 24 public class UserController{ 25 @Autowired 26 private UserService userService; 27 //获取日志对象 28 private static final transient Logger log = LoggerFactory.getLogger(UserController.class); 29 30 @RequestMapping("/login") 31 public String login(String username,String password,String remFlag){ 32 //创建subject 33 Subject currentUser= SecurityUtils.getSubject(); 34 // currentUser.isAuthenticated()当前用户是否被认证 35 // 如果当前用户被认证了,说明用户还是处于登录状态 36 if (!currentUser.isAuthenticated()) { 37 //用户名密码令牌:将表单中的用户名和密码封装进token中 38 UsernamePasswordToken token = new UsernamePasswordToken(username, password); 39 //记住我 40 41 token.setRememberMe(true); 42 43 try { 44 // 调用subject中的login方法来进行匹配用户是否可以登录成功 45 // login方法的参数需要接收shiro的UsernamePasswordToken类型 46 currentUser.login(token);//找到xml文件中id=shiroFilter对应的realm 47 } catch (UnknownAccountException uae) {//账号不存在 48 log.info("There is no user with username of " + token.getPrincipal()); 49 throw new UnknownAccountException("账号不存在"); 50 // return "login"; 51 } catch (IncorrectCredentialsException ice) {//密码错误 52 log.info("Password for account " + token.getPrincipal() + " was incorrect!"); 53 throw new IncorrectCredentialsException("密码错误"); 54 // return "login"; 55 } catch (LockedAccountException lae) {//账号锁死 56 log.info("The account for username " + token.getPrincipal() + " is locked. " + 57 "Please contact your administrator to unlock it."); 58 throw new LockedAccountException("账号锁死"); 59 } 60 catch (AuthenticationException ae) { 61 log.info(("The account form username"+token.getPrincipal()+"is locked!")); 62 throw new AuthenticationException("账号锁死"); 63 } 64 } 65 return "front/ok"; 66 } 67 68 @GetMapping("/delete/{id}") 69 @ResponseBody 70 @RequiresPermissions("emp/delete") 71 public boolean delete(@PathVariable("id") Integer id){ 72 int result=userService.deleteByPrimaryKey(id); 73 return result>0?true:false; 74 } 75 }
shiro的多次登录错误下账号锁死
思路:利用缓存记录登录的信息,如果错误次数达到指定数,就抛出异常
实现:
1、在资源文件目录下新建一个定义缓存的xml:ehcache.xml,定义缓存的相关信息
正常情况下的默认缓存是:
1 <!-- 默认缓存 --> 2 <defaultCache 3 maxEntriesLocalHeap="10000" 4 eternal="false" 5 timeToIdleSeconds="120" 6 timeToLiveSeconds="120" 7 maxEntriesLocalDisk="10000000" 8 diskExpiryThreadIntervalSeconds="120" 9 memoryStoreEvictionPolicy="LRU"/>
自定义缓存
1 <!-- 登录记录缓存 锁定10分钟 --> 2 <cache name="passwordRetryCache" 3 maxEntriesLocalHeap="2000" 4 eternal="false" 5 timeToIdleSeconds="360" 6 timeToLiveSeconds="360" 7 overflowToDisk="false" 8 statistics="true"> 9 </cache>
以上属性的含义:
maxEntriesLocalHeap:是用来限制当前缓存在堆内存上所能保存的最大元素数量
eternal:false 设定缓存的elemen是否永远不过期
timeToLiveSeconds:对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值为0,表示一直可以访问。(单位:秒)
timeToIdleSeconds:对象空闲时,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值为0。(单位:秒)
新定义一个类
1 package com.aaa.credentials; 2 3 import com.aaa.entity.Result; 4 import org.apache.shiro.authc.AuthenticationException; 5 import org.apache.shiro.authc.AuthenticationInfo; 6 import org.apache.shiro.authc.AuthenticationToken; 7 import org.apache.shiro.authc.ExcessiveAttemptsException; 8 import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 9 import org.apache.shiro.cache.Cache; 10 import org.apache.shiro.cache.CacheManager; 11 12 import java.util.concurrent.atomic.AtomicInteger; 13 14 public class MyMatcher extends HashedCredentialsMatcher { 15 16 //Map:key,value 17 //key:存用户名 value:次数 18 private Cache<String, AtomicInteger> passwordCache; 19 20 public MyMatcher(CacheManager cacheManager) { 21 this.passwordCache = cacheManager.getCache("passwordRetryCache"); 22 } 23 24 //密码匹配 25 @Override 26 public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { 27 //获取用户名 28 String username= (String) token.getPrincipal(); 29 //先去缓存中查找是否有该信息 30 AtomicInteger retryCount= passwordCache.get(username); 31 //第一次是null 32 if(retryCount==null){ 33 //初始话:0 34 retryCount=new AtomicInteger(0); 35 //存入缓存中 36 passwordCache.put(username,retryCount); 37 } 38 //在retryCount上增加1,并获取该值,当第3次仍然输入错误则锁死账号 39 if(retryCount.incrementAndGet()>2){ 40 throw new ExcessiveAttemptsException("该账号已锁定"); 41 } 42 //密码匹配 43 boolean matcher=super.doCredentialsMatch(token,info); 44 //如果登录成功 45 if(matcher){ 46 //清空缓存数据 47 passwordCache.remove(username); 48 } 49 return matcher; 50 } 51 }
控制类中
1 package com.aaa.controller; 2 3 import com.aaa.service.UserService; 4 import org.apache.shiro.SecurityUtils; 5 import org.apache.shiro.authc.*; 6 import org.apache.shiro.authz.annotation.RequiresPermissions; 7 import org.apache.shiro.subject.Subject; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Controller; 12 import org.springframework.web.bind.annotation.*; 13 14 import java.util.List; 15 16 /** 17 * @Author 刘其佳 18 * 2019/8/12 -- 16:43 19 * @Version 1.0 20 */ 21 @Controller 22 //@RequestMapping("/user") 23 //public class UserController extends BaseController{ 24 public class UserController{ 25 @Autowired 26 private UserService userService; 27 //获取日志对象 28 private static final transient Logger log = LoggerFactory.getLogger(UserController.class); 29 30 @RequestMapping("/login") 31 public String login(String username,String password,String remFlag){ 32 //创建subject 33 Subject currentUser= SecurityUtils.getSubject(); 34 // currentUser.isAuthenticated()当前用户是否被认证 35 // 如果当前用户被认证了,说明用户还是处于登录状态 36 if (!currentUser.isAuthenticated()) { 37 //用户名密码令牌:将表单中的用户名和密码封装进token中 38 UsernamePasswordToken token = new UsernamePasswordToken(username, password); 39 //记住我(如果有值,就存入cookie) 40 if(remFlag.equals("1")){ 41 token.setRememberMe(true); 42 } 43 try { 44 // 调用subject中的login方法来进行匹配用户是否可以登录成功 45 // login方法的参数需要接收shiro的UsernamePasswordToken类型 46 currentUser.login(token);//找到xml文件中id=shiroFilter对应的realm 47 } catch (UnknownAccountException uae) {//账号不存在 48 log.info("There is no user with username of " + token.getPrincipal()); 49 throw new UnknownAccountException("账号不存在"); 50 // return "login"; 51 } catch (IncorrectCredentialsException ice) {//密码错误 52 log.info("Password for account " + token.getPrincipal() + " was incorrect!"); 53 throw new IncorrectCredentialsException("密码错误"); 54 // return "login"; 55 } catch (LockedAccountException lae) {//账号锁死 56 log.info("The account for username " + token.getPrincipal() + " is locked. " + 57 "Please contact your administrator to unlock it."); 58 throw new LockedAccountException("账号锁死"); 59 } 60 catch (AuthenticationException ae) { 61 log.info(("The account form username"+token.getPrincipal()+"is locked!")); 62 throw new AuthenticationException("账号锁死"); 63 } 64 } 65 return "front/ok"; 66 } 67 68 @GetMapping("/delete/{id}") 69 @ResponseBody 70 @RequiresPermissions("emp/delete") 71 public boolean delete(@PathVariable("id") Integer id){ 72 int result=userService.deleteByPrimaryKey(id); 73 return result>0?true:false; 74 } 75 }
realm:
1 package com.aaa.realm; 2 3 import com.aaa.service.UserService; 4 import org.apache.shiro.authc.*; 5 import org.apache.shiro.crypto.hash.SimpleHash; 6 import org.apache.shiro.realm.AuthenticatingRealm; 7 import org.apache.shiro.util.ByteSource; 8 import org.springframework.beans.factory.annotation.Autowired; 9 10 /** 11 * @Author 刘其佳 12 * 2019/8/12 -- 16:28 13 * @Version 1.0 14 */ 15 16 17 public class MyRealm extends AuthenticatingRealm { 18 19 @Autowired 20 private UserService userService; 21 22 /** 23 * @param authenticationToken:封装的身份信息(表单中传过来的) 24 * @return 25 * @throws AuthenticationException 26 */ 27 @Override 28 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 29 /** 30 * 1、获取subject传递过来的token 31 * 2、根据token的用户名找到数据库中对应的密码 32 * 3、返回认证对象 33 */ 34 UsernamePasswordToken usernamePasswordToken= (UsernamePasswordToken) authenticationToken; 35 //获取令牌中的用户名 36 String username=usernamePasswordToken.getUsername(); 37 //连接数据库根据用户名查询密码 38 String password=userService.selectByName(username); 39 // 加盐 40 ByteSource salt=ByteSource.Util.bytes(username); 41 //返回认证信息:最后一个参数是当前realm的名字,两种写法 42 // SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(username,password,"MyRealm"); 43 SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(username,password,salt,getName()); 44 45 46 return info; 47 } 48 49 public static void main(String[] args){ 50 //盐:salt 51 String username="admin"; 52 String password="123456"; 53 ByteSource salt=ByteSource.Util.bytes(username); 54 SimpleHash simpleHash=new SimpleHash("MD5",password,salt); 55 String str=simpleHash.toHex(); 56 System.out.println(str); 57 } 58 }