├── README.md ├── .gitignore ├── src ├── test │ └── java │ │ └── net │ │ └── ameizi │ │ └── ApplicationTests.java └── main │ ├── java │ └── net │ │ └── ameizi │ │ ├── shiro │ │ ├── LoginUser.java │ │ ├── jwt │ │ │ ├── JwtToken.java │ │ │ └── JwtRealm.java │ │ ├── StatelessDefaultSubjectFactory.java │ │ └── filter │ │ │ ├── JcaptchaValidateFilter.java │ │ │ └── JwtAuthenticationFilter.java │ │ ├── utils │ │ ├── HttpStatus.java │ │ ├── ApplicationContextUtil.java │ │ ├── SubjectUtil.java │ │ └── TokenUtil.java │ │ ├── domain │ │ ├── SysUserRole.java │ │ ├── SysRoleResource.java │ │ ├── SysRole.java │ │ ├── SysResource.java │ │ └── SysUser.java │ │ ├── config │ │ ├── RedisConfig.java │ │ ├── KaptchaConfig.java │ │ ├── SwaggerConfiguration.java │ │ └── ShiroConfig.java │ │ ├── cors │ │ └── CorsConfig.java │ │ ├── Application.java │ │ └── controller │ │ ├── BaseController.java │ │ └── LoginController.java │ └── resources │ ├── application.yml │ ├── generatorConfig.xml │ └── shiro.sql ├── mvnw.cmd ├── pom.xml └── mvnw /README.md: -------------------------------------------------------------------------------- 1 | # shiro-jwt-springboot 2 | shiro整合jwt前后端分离权限认证示例 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /src/test/java/net/ameizi/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package net.ameizi; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class ApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/LoginUser.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @AllArgsConstructor 11 | @Builder 12 | public class LoginUser { 13 | private String username; 14 | private String password; 15 | private String vcode; 16 | String vcodeKey; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | jackson: 4 | serialization: 5 | INDENT_OUTPUT: true 6 | date-format: yyyy-MM-dd HH:mm:ss 7 | joda-date-time-format: yyyy-MM-dd HH:mm:ss 8 | default-property-inclusion: non_null 9 | time-zone: GMT+8 10 | redis: 11 | host: localhost 12 | port: 6379 13 | database: 0 14 | password: jpdbb1387lipushdatabseredis 15 | 16 | swagger: 17 | host: localhost:8080 18 | 19 | jwt: 20 | # header: Authorization 21 | token: 22 | secret: mySecret 23 | # 7天 24 | # expiration: 604800 25 | 26 | # 一天 27 | expiration: 86400 -------------------------------------------------------------------------------- /src/main/java/net/ameizi/utils/HttpStatus.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.utils; 2 | 3 | public enum HttpStatus { 4 | 5 | OK(200, "请求成功"), 6 | BAD_REQUEST(400, "请求出错"), 7 | UNAUTHORIZED(401, "没有登录"), 8 | FORBIDDEN(403, "没有权限"), 9 | NOT_FOUND(404, "找不到页面"), 10 | INTERNAL_SERVER_ERROR(500, "服务器出错"); 11 | 12 | private final int value; 13 | 14 | private final String msg; 15 | 16 | HttpStatus(int value, String msg) { 17 | this.value = value; 18 | this.msg = msg; 19 | } 20 | 21 | public int value() { 22 | return value; 23 | } 24 | 25 | public String msg() { 26 | return msg; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/jwt/JwtToken.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro.jwt; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.apache.shiro.authc.AuthenticationToken; 8 | 9 | @Data 10 | @Builder 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class JwtToken implements AuthenticationToken { 14 | 15 | private String principal; 16 | 17 | private String token; 18 | 19 | @Override 20 | public String getPrincipal() { 21 | return principal; 22 | } 23 | 24 | @Override 25 | public Object getCredentials() { 26 | return token; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/domain/SysUserRole.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.domain; 2 | 3 | public class SysUserRole { 4 | private Long id; 5 | 6 | private Long userId; 7 | 8 | private Long roleId; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public Long getUserId() { 19 | return userId; 20 | } 21 | 22 | public void setUserId(Long userId) { 23 | this.userId = userId; 24 | } 25 | 26 | public Long getRoleId() { 27 | return roleId; 28 | } 29 | 30 | public void setRoleId(Long roleId) { 31 | this.roleId = roleId; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/StatelessDefaultSubjectFactory.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro; 2 | 3 | import org.apache.shiro.subject.Subject; 4 | import org.apache.shiro.subject.SubjectContext; 5 | import org.apache.shiro.web.mgt.DefaultWebSubjectFactory; 6 | 7 | /** 8 | * 通过调用context.setSessionCreationEnabled(false)表示不创建会话, 9 | * 如果之后调用Subject.getSession()将抛出DisabledSessionException异常。 10 | */ 11 | public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory { 12 | 13 | @Override 14 | public Subject createSubject(SubjectContext context) { 15 | // 不创建session 16 | context.setSessionCreationEnabled(false); 17 | return super.createSubject(context); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/domain/SysRoleResource.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.domain; 2 | 3 | public class SysRoleResource { 4 | private Long id; 5 | 6 | private Long roleId; 7 | 8 | private Long resourceId; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public Long getRoleId() { 19 | return roleId; 20 | } 21 | 22 | public void setRoleId(Long roleId) { 23 | this.roleId = roleId; 24 | } 25 | 26 | public Long getResourceId() { 27 | return resourceId; 28 | } 29 | 30 | public void setResourceId(Long resourceId) { 31 | this.resourceId = resourceId; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/net/ameizi/utils/ApplicationContextUtil.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class ApplicationContextUtil implements ApplicationContextAware { 10 | 11 | private static ApplicationContext applicationContext; 12 | 13 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 14 | ApplicationContextUtil.applicationContext = applicationContext; 15 | } 16 | 17 | public static T getBean(Class cls){ 18 | return applicationContext.getBean(cls); 19 | } 20 | 21 | public static Object getBeanObj(String beanName){ 22 | return applicationContext.getBean(beanName); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | 9 | @Configuration 10 | public class RedisConfig { 11 | 12 | @Value("${spring.redis.host}") 13 | private String host; 14 | 15 | @Value("${spring.redis.password}") 16 | private String password; 17 | 18 | @Value("${spring.redis.port}") 19 | private Integer port = 6379; 20 | 21 | @Value("${spring.redis.database}") 22 | private Integer database = 0; 23 | 24 | @Bean 25 | JedisConnectionFactory jedisConnectionFactory() { 26 | JedisConnectionFactory connectionFactory = new JedisConnectionFactory(); 27 | connectionFactory.setHostName(host); 28 | connectionFactory.setPassword(password); 29 | connectionFactory.setPort(port); 30 | connectionFactory.setDatabase(database); 31 | return connectionFactory; 32 | } 33 | 34 | @Bean("redisTemplate") 35 | public RedisTemplate redisTemplate() { 36 | RedisTemplate redisTemplate = new RedisTemplate(); 37 | redisTemplate.setConnectionFactory(jedisConnectionFactory()); 38 | return redisTemplate; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/cors/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.cors; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.cors.CorsConfiguration; 6 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 7 | import org.springframework.web.filter.CorsFilter; 8 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 11 | 12 | @Configuration 13 | public class CorsConfig { 14 | 15 | @Bean 16 | public WebMvcConfigurer corsConfigurer() { 17 | return new WebMvcConfigurerAdapter() { 18 | @Override 19 | public void addCorsMappings(CorsRegistry registry) { 20 | registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "HEAD", "DELETE", "OPTION"); 21 | } 22 | }; 23 | } 24 | 25 | private CorsConfiguration buildConfig() { 26 | CorsConfiguration corsConfiguration = new CorsConfiguration(); 27 | corsConfiguration.addAllowedOrigin("*"); 28 | corsConfiguration.addAllowedHeader("*"); 29 | corsConfiguration.addAllowedMethod("*"); 30 | return corsConfiguration; 31 | } 32 | 33 | @Bean 34 | public CorsFilter corsFilter() { 35 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 36 | source.registerCorsConfiguration("/**", buildConfig()); 37 | return new CorsFilter(source); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/utils/SubjectUtil.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.utils; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.util.StringUtils; 7 | 8 | @Component 9 | public class SubjectUtil { 10 | 11 | @Autowired 12 | private TokenUtil tokenUtil; 13 | 14 | public Integer getSubjectId() { 15 | String username = (String) SecurityUtils.getSubject().getPrincipal(); 16 | if (StringUtils.isEmpty(username)) { 17 | throw new IllegalArgumentException("该用户未登录"); 18 | } 19 | return 1; 20 | } 21 | 22 | 23 | /** 24 | * 直接从token获取用户id,不抛异常。 25 | * @param token token 26 | * @return 用户id 27 | */ 28 | // public Long getSubjectIdFromToken(String token) { 29 | // User user = this.getSubjectUserFromToken(token); 30 | // return user.getId(); 31 | // } 32 | 33 | 34 | /** 35 | * 直接从token获取用户id,不抛异常。 36 | * @param token token 37 | * @return 用户id 38 | */ 39 | // public User getSubjectUserFromToken(String token) { 40 | // if(StringUtils.isEmpty(token)){ 41 | // throw new TokenParserException("令牌为空!"); 42 | // } 43 | // 44 | // String username = tokenUtil.getUsernameFromToken(token); 45 | // 46 | // if (StringUtils.isEmpty(username)) { 47 | // throw new TokenParserException("从令牌中解析用户名为空!"); 48 | // } 49 | // User user = this.userService.selectOne(User.builder().username(username).build()); 50 | // if (user == null) { 51 | // throw new LoginException("用户不存在!"); 52 | // } 53 | // return user; 54 | // } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/Application.java: -------------------------------------------------------------------------------- 1 | package net.ameizi; 2 | 3 | import com.alibaba.fastjson.serializer.SerializerFeature; 4 | import com.alibaba.fastjson.support.config.FastJsonConfig; 5 | import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.autoconfigure.web.HttpMessageConverters; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.http.converter.HttpMessageConverter; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | @SpringBootApplication 17 | public class Application { 18 | 19 | public static void main(String[] args) { 20 | SpringApplication.run(Application.class, args); 21 | } 22 | 23 | 24 | /** 25 | * 1.需要定义一个convert转换消息的对象 26 | * 2.创建配置信息,加入配置信息:比如是否需要格式化返回的json 27 | * 3.converter中添加配置信息 28 | * 4.convert添加到converters当中 29 | */ 30 | @Bean 31 | public HttpMessageConverters fastJsonHttpMessageConverters() { 32 | // 1.需要定义一个convert转换消息的对象 33 | FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); 34 | 35 | //2.创建配置信息,加入配置信息:比如是否需要格式化返回的json 36 | FastJsonConfig fastJsonConfig = new FastJsonConfig(); 37 | fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); 38 | 39 | //3.converter中添加配置信息 40 | fastConverter.setFastJsonConfig(fastJsonConfig); 41 | 42 | /** 43 | * 设置json 返回格式和编码方式 处理乱码问题 44 | */ 45 | List mediaTypeList=new ArrayList<>(); 46 | mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8); 47 | fastConverter.setSupportedMediaTypes(mediaTypeList); 48 | 49 | //4.convert添加到converters当中 50 | HttpMessageConverter converter = fastConverter; 51 | return new HttpMessageConverters(converter); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/config/KaptchaConfig.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.config; 2 | 3 | import com.google.code.kaptcha.impl.DefaultKaptcha; 4 | import com.google.code.kaptcha.util.Config; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | import java.util.Properties; 9 | 10 | @Configuration 11 | public class KaptchaConfig { 12 | 13 | @Bean 14 | public DefaultKaptcha captchaProducer(){ 15 | DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); 16 | 17 | Properties properties = new Properties(); 18 | //验证码宽度 19 | properties.setProperty("kaptcha.image.width","115"); 20 | //验证码高度 21 | properties.setProperty("kaptcha.image.height","35"); 22 | //生成验证码内容范围 23 | properties.setProperty("kaptcha.textproducer.char.string","abcde2345678gfynmnpwx"); 24 | // 验证码个数 25 | properties.setProperty("kaptcha.textproducer.char.length","5"); 26 | //是否有边框 27 | properties.setProperty("kaptcha.border","no"); 28 | //验证码字体颜色 29 | properties.setProperty("kaptcha.textproducer.font.color","red"); 30 | //验证码字体大小 31 | properties.setProperty("kaptcha.textproducer.font.size","24"); 32 | // 验证码所属字体样式 33 | properties.setProperty("kaptcha.textproducer.font.names","Arial, Courier"); 34 | properties.setProperty("kaptcha.background.clear.from","white"); 35 | properties.setProperty("kaptcha.background.clear.to","white"); 36 | properties.setProperty("kaptcha.obscurificator.impl","com.google.code.kaptcha.impl.ShadowGimpy"); 37 | properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise"); 38 | //干扰线颜色 39 | properties.setProperty("kaptcha.noise.color","red"); 40 | //验证码文本字符间距 41 | properties.setProperty("kaptcha.textproducer.char.space","3"); 42 | 43 | Config Config = new Config(properties); 44 | defaultKaptcha.setConfig(Config); 45 | 46 | return defaultKaptcha; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/domain/SysRole.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.domain; 2 | 3 | import java.util.Date; 4 | 5 | public class SysRole { 6 | private Long id; 7 | 8 | private String name; 9 | 10 | private String code; 11 | 12 | private String remark; 13 | 14 | private Boolean status; 15 | 16 | private Long parentId; 17 | 18 | private Long createBy; 19 | 20 | private Date createAt; 21 | 22 | private Long updateBy; 23 | 24 | private Date updateAt; 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name == null ? null : name.trim(); 40 | } 41 | 42 | public String getCode() { 43 | return code; 44 | } 45 | 46 | public void setCode(String code) { 47 | this.code = code == null ? null : code.trim(); 48 | } 49 | 50 | public String getRemark() { 51 | return remark; 52 | } 53 | 54 | public void setRemark(String remark) { 55 | this.remark = remark == null ? null : remark.trim(); 56 | } 57 | 58 | public Boolean getStatus() { 59 | return status; 60 | } 61 | 62 | public void setStatus(Boolean status) { 63 | this.status = status; 64 | } 65 | 66 | public Long getParentId() { 67 | return parentId; 68 | } 69 | 70 | public void setParentId(Long parentId) { 71 | this.parentId = parentId; 72 | } 73 | 74 | public Long getCreateBy() { 75 | return createBy; 76 | } 77 | 78 | public void setCreateBy(Long createBy) { 79 | this.createBy = createBy; 80 | } 81 | 82 | public Date getCreateAt() { 83 | return createAt; 84 | } 85 | 86 | public void setCreateAt(Date createAt) { 87 | this.createAt = createAt; 88 | } 89 | 90 | public Long getUpdateBy() { 91 | return updateBy; 92 | } 93 | 94 | public void setUpdateBy(Long updateBy) { 95 | this.updateBy = updateBy; 96 | } 97 | 98 | public Date getUpdateAt() { 99 | return updateAt; 100 | } 101 | 102 | public void setUpdateAt(Date updateAt) { 103 | this.updateAt = updateAt; 104 | } 105 | } -------------------------------------------------------------------------------- /src/main/resources/generatorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 |
55 |
56 |
57 | 58 | 59 | 64 |
65 |
-------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/filter/JcaptchaValidateFilter.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro.filter; 2 | 3 | import net.ameizi.utils.ApplicationContextUtil; 4 | import org.apache.shiro.web.filter.AccessControlFilter; 5 | import org.apache.shiro.web.util.WebUtils; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.util.StringUtils; 8 | 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | 13 | public class JcaptchaValidateFilter extends AccessControlFilter { 14 | 15 | private boolean jcaptchaEbabled = true; //是否开启验证码支持 16 | 17 | private String jcaptchaParam = "jcaptchaCode"; //前台提交的验证码参数名 18 | 19 | public static String failureKeyAttribute = "jcaptcha.message"; //验证失败后存储到的属性名 20 | 21 | private String redisCaptchaKey = "vcodeKey"; 22 | 23 | public void setJcaptchaEbabled(boolean jcaptchaEbabled) { 24 | this.jcaptchaEbabled = jcaptchaEbabled; 25 | } 26 | 27 | public void setJcaptchaParam(String jcaptchaParam) { 28 | this.jcaptchaParam = jcaptchaParam; 29 | } 30 | 31 | public void setFailureKeyAttribute(String failureKeyAttribute) { 32 | JcaptchaValidateFilter.failureKeyAttribute = failureKeyAttribute; 33 | } 34 | 35 | public void setRedisCaptchaKey(String redisCaptchaKey) { 36 | this.redisCaptchaKey = redisCaptchaKey; 37 | } 38 | 39 | @Override 40 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse servletResponse, Object o) throws Exception { 41 | //1、设置验证码是否开启属性,页面可以根据该属性来决定是否显示验证码 42 | request.setAttribute("jcaptchaEbabled",jcaptchaEbabled); 43 | HttpServletRequest httpRequest = WebUtils.toHttp(request); 44 | 45 | //2、判断验证码是否禁用或不是表单提交(允许访问) 46 | if(!jcaptchaEbabled || !"post".equalsIgnoreCase(httpRequest.getMethod())){ 47 | return true; 48 | } 49 | String captchaKey = httpRequest.getParameter(redisCaptchaKey); 50 | if(StringUtils.isEmpty(captchaKey)){ 51 | request.setAttribute(failureKeyAttribute,"验证码Key为空!"); 52 | return false; 53 | } 54 | 55 | RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBeanObj("redisTemplate"); 56 | String captchaValue = (String) redisTemplate.boundValueOps(captchaKey).get(); 57 | 58 | if(StringUtils.isEmpty(captchaValue)){ 59 | request.setAttribute(failureKeyAttribute,"验证码已过期!"); 60 | return false; 61 | } 62 | 63 | if((httpRequest.getParameter(jcaptchaParam)).equalsIgnoreCase(captchaValue)){ 64 | return true; 65 | } 66 | 67 | request.setAttribute(failureKeyAttribute,"验证码输入错误!"); 68 | 69 | return false; 70 | } 71 | 72 | @Override 73 | protected boolean onAccessDenied(ServletRequest request, ServletResponse servletResponse) throws Exception { 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/domain/SysResource.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.domain; 2 | 3 | import java.util.Date; 4 | 5 | public class SysResource { 6 | private Long id; 7 | 8 | private String type; 9 | 10 | private String name; 11 | 12 | private String permission; 13 | 14 | private String icon; 15 | 16 | private Integer sort; 17 | 18 | private String url; 19 | 20 | private String description; 21 | 22 | private Boolean status; 23 | 24 | private Long parentId; 25 | 26 | private Long createBy; 27 | 28 | private Date createAt; 29 | 30 | private Long updateBy; 31 | 32 | private Date updateAt; 33 | 34 | public Long getId() { 35 | return id; 36 | } 37 | 38 | public void setId(Long id) { 39 | this.id = id; 40 | } 41 | 42 | public String getType() { 43 | return type; 44 | } 45 | 46 | public void setType(String type) { 47 | this.type = type == null ? null : type.trim(); 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public void setName(String name) { 55 | this.name = name == null ? null : name.trim(); 56 | } 57 | 58 | public String getPermission() { 59 | return permission; 60 | } 61 | 62 | public void setPermission(String permission) { 63 | this.permission = permission == null ? null : permission.trim(); 64 | } 65 | 66 | public String getIcon() { 67 | return icon; 68 | } 69 | 70 | public void setIcon(String icon) { 71 | this.icon = icon == null ? null : icon.trim(); 72 | } 73 | 74 | public Integer getSort() { 75 | return sort; 76 | } 77 | 78 | public void setSort(Integer sort) { 79 | this.sort = sort; 80 | } 81 | 82 | public String getUrl() { 83 | return url; 84 | } 85 | 86 | public void setUrl(String url) { 87 | this.url = url == null ? null : url.trim(); 88 | } 89 | 90 | public String getDescription() { 91 | return description; 92 | } 93 | 94 | public void setDescription(String description) { 95 | this.description = description == null ? null : description.trim(); 96 | } 97 | 98 | public Boolean getStatus() { 99 | return status; 100 | } 101 | 102 | public void setStatus(Boolean status) { 103 | this.status = status; 104 | } 105 | 106 | public Long getParentId() { 107 | return parentId; 108 | } 109 | 110 | public void setParentId(Long parentId) { 111 | this.parentId = parentId; 112 | } 113 | 114 | public Long getCreateBy() { 115 | return createBy; 116 | } 117 | 118 | public void setCreateBy(Long createBy) { 119 | this.createBy = createBy; 120 | } 121 | 122 | public Date getCreateAt() { 123 | return createAt; 124 | } 125 | 126 | public void setCreateAt(Date createAt) { 127 | this.createAt = createAt; 128 | } 129 | 130 | public Long getUpdateBy() { 131 | return updateBy; 132 | } 133 | 134 | public void setUpdateBy(Long updateBy) { 135 | this.updateBy = updateBy; 136 | } 137 | 138 | public Date getUpdateAt() { 139 | return updateAt; 140 | } 141 | 142 | public void setUpdateAt(Date updateAt) { 143 | this.updateAt = updateAt; 144 | } 145 | } -------------------------------------------------------------------------------- /src/main/java/net/ameizi/config/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.config; 2 | 3 | import com.google.common.collect.Lists; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 8 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 | import springfox.documentation.builders.ApiInfoBuilder; 11 | import springfox.documentation.builders.ParameterBuilder; 12 | import springfox.documentation.builders.PathSelectors; 13 | import springfox.documentation.builders.RequestHandlerSelectors; 14 | import springfox.documentation.schema.ModelRef; 15 | import springfox.documentation.service.ApiInfo; 16 | import springfox.documentation.service.Contact; 17 | import springfox.documentation.service.Parameter; 18 | import springfox.documentation.spi.DocumentationType; 19 | import springfox.documentation.spring.web.plugins.Docket; 20 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 21 | 22 | import java.util.List; 23 | 24 | @Configuration 25 | @EnableSwagger2 26 | public class SwaggerConfiguration extends WebMvcConfigurerAdapter { 27 | 28 | @Value("${swagger.host}") 29 | private String swaggerHost; 30 | ApiInfo apiInfo() { 31 | return new ApiInfoBuilder() 32 | .title("amz") 33 | .description("基于shiro和jwt的前后端分离权限系统") 34 | .termsOfServiceUrl("") 35 | .version("1.0.0") 36 | .contact(new Contact("", "", "")) 37 | .build(); 38 | } 39 | 40 | @Bean 41 | public Docket createRestApi() { 42 | ParameterBuilder builder = new ParameterBuilder(); 43 | Parameter parameter = builder 44 | // 从cookie中获取token 45 | .parameterType("cookie") //参数类型支持header, cookie, body, query etc 46 | .name("token") //参数名 47 | .defaultValue("") //默认值 48 | .description("请输入token") 49 | .modelRef(new ModelRef("string")) //指定参数值的类型 50 | .required(false).build(); //非必需,这里是全局配置,然而在登陆的时候是不用验证的 51 | List parameters = Lists.newArrayList(parameter); 52 | return new Docket(DocumentationType.SWAGGER_2) 53 | .host(this.swaggerHost) 54 | .select() 55 | .apis(RequestHandlerSelectors.basePackage("net.ameizi")) 56 | .paths(PathSelectors.any()) 57 | .build() 58 | .apiInfo(this.apiInfo()) 59 | .globalOperationParameters(parameters); 60 | } 61 | 62 | /** 63 | * swagger ui资源映射 64 | * @param registry 65 | */ 66 | @Override 67 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 68 | registry.addResourceHandler("swagger-ui.html") 69 | .addResourceLocations("classpath:/META-INF/resources/"); 70 | registry.addResourceHandler("/webjars*") 71 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 72 | } 73 | 74 | /** 75 | * swagger-ui.html路径映射,浏览器中使用/api-docs访问 76 | * @param registry 77 | */ 78 | @Override 79 | public void addViewControllers(ViewControllerRegistry registry) { 80 | registry.addRedirectViewController("/api-docs","/swagger-ui.html"); 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/filter/JwtAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro.filter; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import net.ameizi.shiro.jwt.JwtToken; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.apache.shiro.authc.AuthenticationException; 8 | import org.apache.shiro.authc.AuthenticationToken; 9 | import org.apache.shiro.subject.Subject; 10 | import org.apache.shiro.web.filter.authc.AuthenticatingFilter; 11 | 12 | import javax.servlet.ServletRequest; 13 | import javax.servlet.ServletResponse; 14 | import javax.servlet.http.Cookie; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | public class JwtAuthenticationFilter extends AuthenticatingFilter { 20 | 21 | private static final String TOKEN = "token"; 22 | 23 | @Override 24 | protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { 25 | HttpServletRequest httpRequest = (HttpServletRequest) request; 26 | // 先从Header里面获取 27 | String token = httpRequest.getHeader(TOKEN); 28 | if(StringUtils.isEmpty(token)){ 29 | // 获取不到再从Parameter中拿 30 | token = httpRequest.getParameter(TOKEN); 31 | // 还是获取不到再从Cookie中拿 32 | if(StringUtils.isEmpty(token)){ 33 | Cookie[] cookies = httpRequest.getCookies(); 34 | if(cookies != null){ 35 | for (Cookie cookie : cookies) { 36 | if(TOKEN.equals(cookie.getName())){ 37 | token = cookie.getValue(); 38 | break; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | return JwtToken.builder() 45 | .token(token) 46 | .build(); 47 | } 48 | 49 | @Override 50 | protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 51 | return false; 52 | } 53 | 54 | @Override 55 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { 56 | return executeLogin(request, response); 57 | } 58 | 59 | @Override 60 | protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, 61 | ServletResponse response) throws Exception { 62 | return true; 63 | } 64 | 65 | @Override 66 | protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, 67 | ServletResponse response) { 68 | HttpServletResponse servletResponse = (HttpServletResponse) response; 69 | JSONObject jsonObject = new JSONObject(); 70 | jsonObject.put("code", HttpServletResponse.SC_UNAUTHORIZED); 71 | jsonObject.put("msg","登录失败,无权访问"); 72 | jsonObject.put("timestamp", System.currentTimeMillis()); 73 | try { 74 | servletResponse.setCharacterEncoding("UTF-8"); 75 | servletResponse.setContentType("application/json;charset=UTF-8"); 76 | servletResponse.setHeader("Access-Control-Allow-Origin","*"); 77 | ObjectMapper objectMapper = new ObjectMapper(); 78 | response.getWriter().write(objectMapper.writeValueAsString(jsonObject)); 79 | } catch (IOException e) { 80 | } 81 | return false; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/shiro/jwt/JwtRealm.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.shiro.jwt; 2 | 3 | import com.google.common.collect.Sets; 4 | import net.ameizi.domain.SysUser; 5 | import net.ameizi.utils.TokenUtil; 6 | import org.apache.shiro.authc.*; 7 | import org.apache.shiro.authz.AuthorizationInfo; 8 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 9 | import org.apache.shiro.realm.AuthorizingRealm; 10 | import org.apache.shiro.subject.PrincipalCollection; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | 13 | import static org.apache.shiro.web.filter.mgt.DefaultFilter.user; 14 | 15 | public class JwtRealm extends AuthorizingRealm { 16 | 17 | @Autowired 18 | private TokenUtil tokenUtil; 19 | 20 | @Override 21 | public boolean supports(AuthenticationToken token) { 22 | //表示此Realm只支持JwtToken类型 23 | return token instanceof JwtToken; 24 | } 25 | 26 | @Override 27 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 28 | // 根据用户名查找角色,请根据需求实现 29 | String username = (String)principals.getPrimaryPrincipal(); 30 | 31 | SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 32 | 33 | // 根据username查询角色 34 | authorizationInfo.setRoles(Sets.newHashSet("admin","superadmin")); 35 | 36 | // 根据username查询权限 37 | authorizationInfo.setStringPermissions(Sets.newHashSet("system:*")); 38 | 39 | return authorizationInfo; 40 | } 41 | 42 | @Override 43 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 44 | JwtToken jwtToken = (JwtToken) authenticationToken; 45 | 46 | // 获取token 47 | String token = jwtToken.getToken(); 48 | 49 | // 从token中获取用户名 50 | String username = tokenUtil.getUsernameFromToken(token); 51 | 52 | // 根据用户名查询数据库 53 | 54 | SysUser user = new SysUser(); 55 | user.setUserName(username); 56 | user.setStatus(1); 57 | user.setPassword("000000"); 58 | 59 | // 用户不存在 60 | if (user == null) { 61 | throw new UnknownAccountException(); 62 | } 63 | 64 | // 用户被禁用 65 | if(user.getStatus()==0){ 66 | throw new LockedAccountException(); 67 | } 68 | 69 | try { 70 | return new SimpleAuthenticationInfo( 71 | username, 72 | token, 73 | getName() 74 | ); 75 | } catch (Exception e) { 76 | throw new AuthenticationException(e); 77 | } 78 | } 79 | 80 | @Override 81 | public void clearCachedAuthorizationInfo(PrincipalCollection principals) { 82 | super.clearCachedAuthorizationInfo(principals); 83 | } 84 | 85 | @Override 86 | public void clearCachedAuthenticationInfo(PrincipalCollection principals) { 87 | super.clearCachedAuthenticationInfo(principals); 88 | } 89 | 90 | @Override 91 | public void clearCache(PrincipalCollection principals) { 92 | super.clearCache(principals); 93 | } 94 | 95 | public void clearAllCachedAuthorizationInfo() { 96 | getAuthorizationCache().clear(); 97 | } 98 | 99 | public void clearAllCachedAuthenticationInfo() { 100 | getAuthenticationCache().clear(); 101 | } 102 | 103 | public void clearAllCache() { 104 | clearAllCachedAuthenticationInfo(); 105 | clearAllCachedAuthorizationInfo(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/domain/SysUser.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.domain; 2 | 3 | import java.util.Date; 4 | 5 | public class SysUser { 6 | private Long id; 7 | 8 | private String mobilePhone; 9 | 10 | private String userName; 11 | 12 | private String nickname; 13 | 14 | private String password; 15 | 16 | private String salt; 17 | 18 | private String signature; 19 | 20 | private Boolean gender; 21 | 22 | private Long qq; 23 | 24 | private String email; 25 | 26 | private String avatar; 27 | 28 | private String province; 29 | 30 | private String city; 31 | 32 | private String regIp; 33 | 34 | private Integer score; 35 | 36 | private Integer status; 37 | 38 | private Long createBy; 39 | 40 | private Date createAt; 41 | 42 | private Long updateBy; 43 | 44 | private Date updateAt; 45 | 46 | public Long getId() { 47 | return id; 48 | } 49 | 50 | public void setId(Long id) { 51 | this.id = id; 52 | } 53 | 54 | public String getMobilePhone() { 55 | return mobilePhone; 56 | } 57 | 58 | public void setMobilePhone(String mobilePhone) { 59 | this.mobilePhone = mobilePhone == null ? null : mobilePhone.trim(); 60 | } 61 | 62 | public String getUserName() { 63 | return userName; 64 | } 65 | 66 | public void setUserName(String userName) { 67 | this.userName = userName == null ? null : userName.trim(); 68 | } 69 | 70 | public String getNickname() { 71 | return nickname; 72 | } 73 | 74 | public void setNickname(String nickname) { 75 | this.nickname = nickname == null ? null : nickname.trim(); 76 | } 77 | 78 | public String getPassword() { 79 | return password; 80 | } 81 | 82 | public void setPassword(String password) { 83 | this.password = password == null ? null : password.trim(); 84 | } 85 | 86 | public String getSalt() { 87 | return salt; 88 | } 89 | 90 | public void setSalt(String salt) { 91 | this.salt = salt == null ? null : salt.trim(); 92 | } 93 | 94 | public String getSignature() { 95 | return signature; 96 | } 97 | 98 | public void setSignature(String signature) { 99 | this.signature = signature == null ? null : signature.trim(); 100 | } 101 | 102 | public Boolean getGender() { 103 | return gender; 104 | } 105 | 106 | public void setGender(Boolean gender) { 107 | this.gender = gender; 108 | } 109 | 110 | public Long getQq() { 111 | return qq; 112 | } 113 | 114 | public void setQq(Long qq) { 115 | this.qq = qq; 116 | } 117 | 118 | public String getEmail() { 119 | return email; 120 | } 121 | 122 | public void setEmail(String email) { 123 | this.email = email == null ? null : email.trim(); 124 | } 125 | 126 | public String getAvatar() { 127 | return avatar; 128 | } 129 | 130 | public void setAvatar(String avatar) { 131 | this.avatar = avatar == null ? null : avatar.trim(); 132 | } 133 | 134 | public String getProvince() { 135 | return province; 136 | } 137 | 138 | public void setProvince(String province) { 139 | this.province = province == null ? null : province.trim(); 140 | } 141 | 142 | public String getCity() { 143 | return city; 144 | } 145 | 146 | public void setCity(String city) { 147 | this.city = city == null ? null : city.trim(); 148 | } 149 | 150 | public String getRegIp() { 151 | return regIp; 152 | } 153 | 154 | public void setRegIp(String regIp) { 155 | this.regIp = regIp == null ? null : regIp.trim(); 156 | } 157 | 158 | public Integer getScore() { 159 | return score; 160 | } 161 | 162 | public void setScore(Integer score) { 163 | this.score = score; 164 | } 165 | 166 | public Integer getStatus() { 167 | return status; 168 | } 169 | 170 | public void setStatus(Integer status) { 171 | this.status = status; 172 | } 173 | 174 | public Long getCreateBy() { 175 | return createBy; 176 | } 177 | 178 | public void setCreateBy(Long createBy) { 179 | this.createBy = createBy; 180 | } 181 | 182 | public Date getCreateAt() { 183 | return createAt; 184 | } 185 | 186 | public void setCreateAt(Date createAt) { 187 | this.createAt = createAt; 188 | } 189 | 190 | public Long getUpdateBy() { 191 | return updateBy; 192 | } 193 | 194 | public void setUpdateBy(Long updateBy) { 195 | this.updateBy = updateBy; 196 | } 197 | 198 | public Date getUpdateAt() { 199 | return updateAt; 200 | } 201 | 202 | public void setUpdateAt(Date updateAt) { 203 | this.updateAt = updateAt; 204 | } 205 | } -------------------------------------------------------------------------------- /src/main/java/net/ameizi/utils/TokenUtil.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.utils; 2 | 3 | import io.jsonwebtoken.*; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.mobile.device.Device; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.Date; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | @Component 13 | public class TokenUtil { 14 | 15 | static final String CLAIM_KEY_USERNAME = "sub"; 16 | static final String CLAIM_KEY_AUDIENCE = "audience"; 17 | static final String CLAIM_KEY_CREATED = "created"; 18 | 19 | private static final String AUDIENCE_UNKNOWN = "unknown"; 20 | private static final String AUDIENCE_WEB = "web"; 21 | private static final String AUDIENCE_MOBILE = "mobile"; 22 | private static final String AUDIENCE_TABLET = "tablet"; 23 | 24 | @Value("${jwt.token.secret}") 25 | private String secret; 26 | 27 | @Value("${jwt.token.expiration}") 28 | private Long expiration; 29 | 30 | 31 | public String getSecret() { 32 | return secret; 33 | } 34 | 35 | public void setSecret(String secret) { 36 | this.secret = secret; 37 | } 38 | 39 | public Long getExpiration() { 40 | return expiration; 41 | } 42 | 43 | public void setExpiration(Long expiration) { 44 | this.expiration = expiration; 45 | } 46 | 47 | private Claims getClaimsFromToken(String token) { 48 | Claims claims; 49 | try { 50 | claims = Jwts.parser() 51 | .setSigningKey(secret) 52 | .parseClaimsJws(token) 53 | .getBody(); 54 | } catch (Exception e) { 55 | claims = null; 56 | } 57 | return claims; 58 | } 59 | 60 | 61 | /** 62 | * 生成token 63 | * @param username 用户名 64 | * @param device org.springframework.mobile.device 设备判断对象 65 | * @return 66 | */ 67 | public String generateToken(String username, Device device) { 68 | Map claims = new HashMap<>(); 69 | claims.put(CLAIM_KEY_USERNAME, username); 70 | claims.put(CLAIM_KEY_AUDIENCE, generateAudience(device)); 71 | claims.put(CLAIM_KEY_CREATED, new Date()); 72 | return generateToken(claims); 73 | } 74 | 75 | private String generateToken(Map claims) { 76 | return Jwts.builder() 77 | .setClaims(claims) 78 | .setExpiration(generateExpirationDate()) 79 | .signWith(SignatureAlgorithm.HS512, this.secret) 80 | .compact(); 81 | } 82 | 83 | /** 84 | * 生成token时间 = 当前时间 + expiration(properties中配置的失效时间) 85 | * @return 86 | */ 87 | private Date generateExpirationDate() { 88 | return new Date(System.currentTimeMillis() + expiration * 1000); 89 | } 90 | 91 | /** 92 | * 通过spring-mobile-device的device检测访问主体 93 | * @param device 94 | * @return 95 | */ 96 | private String generateAudience(Device device) { 97 | String audience = AUDIENCE_UNKNOWN; 98 | if (device.isNormal()) { 99 | audience = AUDIENCE_WEB;//PC端 100 | } else if (device.isTablet()) { 101 | audience = AUDIENCE_TABLET;//平板 102 | } else if (device.isMobile()) { 103 | audience = AUDIENCE_MOBILE;//手机 104 | } 105 | return audience; 106 | } 107 | 108 | /** 109 | * 根据token获取用户名 110 | * @param token 111 | * @return 112 | */ 113 | public String getUsernameFromToken(String token) { 114 | String username; 115 | try { 116 | final Claims claims = getClaimsFromToken(token); 117 | username = claims.getSubject(); 118 | } catch (Exception e) { 119 | username = null; 120 | } 121 | return username; 122 | } 123 | 124 | /** 125 | * 判断token失效时间是否到了 126 | * @param token 127 | * @return 128 | */ 129 | private Boolean isTokenExpired(String token) { 130 | final Date expiration = getExpirationDateFromToken(token); 131 | return expiration.before(new Date()); 132 | } 133 | 134 | /** 135 | * 获取设置的token失效时间 136 | * @param token 137 | * @return 138 | */ 139 | public Date getExpirationDateFromToken(String token) { 140 | Date expiration; 141 | try { 142 | final Claims claims = getClaimsFromToken(token); 143 | expiration = claims.getExpiration(); 144 | } catch (Exception e) { 145 | expiration = null; 146 | } 147 | return expiration; 148 | } 149 | 150 | // /** 151 | // * Token失效校验 152 | // * @param token token字符串 153 | // * @param loginInfo 用户信息 154 | // * @return 155 | // */ 156 | // public Boolean validateToken(String token, LoginInfo loginInfo) { 157 | // final String username = getUsernameFromToken(token); 158 | // return ( 159 | // username.equals(loginInfo.getUsername()) 160 | // && !isTokenExpired(token)); 161 | // } 162 | 163 | public String refreshToken(String token) { 164 | final Claims claims = this.getClaimsFromToken(token); 165 | claims.put(CLAIM_KEY_CREATED, new Date()); 166 | return generateToken(claims); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/controller/BaseController.java: -------------------------------------------------------------------------------- 1 | //package net.ameizi.controller; 2 | // 3 | //import com.google.common.collect.Maps; 4 | //import net.ameizi.utils.HttpStatus; 5 | //import org.apache.shiro.authz.AuthorizationException; 6 | //import org.apache.shiro.authz.UnauthorizedException; 7 | //import org.springframework.http.MediaType; 8 | //import org.springframework.http.ResponseEntity; 9 | //import org.springframework.web.bind.annotation.ExceptionHandler; 10 | // 11 | //import javax.servlet.http.HttpServletRequest; 12 | //import javax.servlet.http.HttpServletResponse; 13 | //import java.util.HashMap; 14 | //import java.util.List; 15 | //import java.util.Map; 16 | // 17 | //public class BaseController { 18 | // 19 | // /** 设置成功响应 */ 20 | // protected ResponseEntity success(Object data) { 21 | // return responseEntity(HttpStatus.OK,data,HttpStatus.OK.msg()); 22 | // } 23 | // 24 | // protected ResponseEntity success(String msg) { 25 | // return responseEntity(HttpStatus.OK,null,msg); 26 | // } 27 | // 28 | // protected ResponseEntity success() { 29 | // return responseEntity(HttpStatus.OK,null,HttpStatus.OK.msg()); 30 | // } 31 | // 32 | // protected ResponseEntity success(Object data,String msg) { 33 | // return responseEntity(HttpStatus.OK,data,msg); 34 | // } 35 | // 36 | // /** 设置失败响应 */ 37 | // protected ResponseEntity error(Object data) { 38 | // return responseEntity(HttpStatus.BAD_REQUEST,data,HttpStatus.BAD_REQUEST.msg()); 39 | // } 40 | // 41 | // protected ResponseEntity error(String msg) { 42 | // return responseEntity(HttpStatus.BAD_REQUEST,null,msg); 43 | // } 44 | // 45 | // protected ResponseEntity error() { 46 | // return responseEntity(HttpStatus.BAD_REQUEST,null,HttpStatus.BAD_REQUEST.msg()); 47 | // } 48 | // 49 | // protected ResponseEntity error(Object data,String msg) { 50 | // return responseEntity(HttpStatus.BAD_REQUEST,data,msg); 51 | // } 52 | // 53 | // /** 设置响应代码 */ 54 | // protected ResponseEntity responseEntity(HttpStatus code, Object data, String msg) { 55 | // Map map = new HashMap<>(); 56 | // if (data != null) { 57 | // if (data instanceof PageInfo) { 58 | // PageInfo page = (PageInfo) data; 59 | // map.put("data", page.getList()); 60 | // map.put("current", page.getPageNum()); 61 | // map.put("size", page.getSize()); 62 | // map.put("pages", page.getPages()); 63 | // map.put("total", page.getTotal()); 64 | // } else if (data instanceof List) { 65 | // map.put("data", data); 66 | // } else { 67 | // map.put("data", data); 68 | // } 69 | // } 70 | // map.put("code", code.value()); 71 | // map.put("msg", msg); 72 | // map.put("timestamp", System.currentTimeMillis()); 73 | // return ResponseEntity.ok() 74 | // .header("Access-Control-Allow-Origin","*") 75 | // .contentType(MediaType.APPLICATION_JSON) 76 | // .body(map); 77 | // } 78 | // 79 | // /** 异常处理 */ 80 | // @ExceptionHandler(Exception.class) 81 | // public void exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) 82 | // throws Exception { 83 | // Map map = Maps.newHashMap(); 84 | // int status; 85 | // // 方法级别shiro权限校验失败时异常信息处理 86 | // if (ex instanceof AuthorizationException){ 87 | // status = HttpStatus.FORBIDDEN.value(); 88 | // map.put("code", HttpStatus.FORBIDDEN.value()); 89 | // map.put("msg", HttpStatus.FORBIDDEN.msg()); 90 | // } else if (ex instanceof UnauthorizedException) { 91 | // status = HttpStatus.FORBIDDEN.value(); 92 | // map.put("code", HttpStatus.FORBIDDEN.value()); 93 | // map.put("msg", HttpStatus.FORBIDDEN.msg()); 94 | // } else if(ex instanceof LoginException){ 95 | // status = HttpStatus.FORBIDDEN.value(); 96 | // map.put("code", HttpStatus.LOGIN_FAIL.value()); 97 | // map.put("msg", ex.getMessage()); 98 | // } else if(ex instanceof UploadException){ 99 | // status = HttpStatus.INTERNAL_SERVER_ERROR.value(); 100 | // map.put("code", HttpStatus.JP_UPLOAD_FAIL.value()); 101 | // map.put("msg", ex.getMessage()); 102 | // } else if(ex instanceof TokenParserException){ 103 | // status = HttpStatus.FORBIDDEN.value(); 104 | // map.put("code", HttpStatus.TOKEN_PARSER_FAIL.value()); 105 | // map.put("msg",ex.getMessage()); 106 | // } else { 107 | // status = HttpStatus.INTERNAL_SERVER_ERROR.value(); 108 | // map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value()); 109 | // map.put("msg", HttpStatus.INTERNAL_SERVER_ERROR.msg()); 110 | // } 111 | // ex.printStackTrace(); 112 | // response.setContentType("application/json;charset=UTF-8"); 113 | // response.setHeader("Access-Control-Allow-Origin","*"); 114 | // response.setStatus(status); 115 | // map.put("timestamp", System.currentTimeMillis()); 116 | // response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes()); 117 | // } 118 | // 119 | //} 120 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | net.ameizi 7 | shiro-jwt-springboot 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | shiro-jwt-springboot 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.7.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.projectlombok 35 | lombok 36 | true 37 | 38 | 39 | 40 | io.jsonwebtoken 41 | jjwt 42 | 0.8.0 43 | 44 | 45 | 46 | org.apache.commons 47 | commons-lang3 48 | 3.6 49 | 50 | 51 | 52 | commons-codec 53 | commons-codec 54 | 1.10 55 | 56 | 57 | 58 | commons-io 59 | commons-io 60 | 2.5 61 | 62 | 63 | 64 | com.google.guava 65 | guava 66 | 23.0 67 | 68 | 69 | 70 | org.apache.shiro 71 | shiro-spring 72 | 1.2.2 73 | 74 | 75 | 76 | org.apache.shiro 77 | shiro-ehcache 78 | 1.2.2 79 | 80 | 81 | 100 | 101 | 102 | org.springframework.boot 103 | spring-boot-starter-data-redis 104 | 105 | 106 | 107 | com.github.axet 108 | kaptcha 109 | 0.0.9 110 | 111 | 112 | 113 | io.springfox 114 | springfox-swagger2 115 | 2.6.1 116 | 117 | 118 | 119 | io.springfox 120 | springfox-swagger-ui 121 | 2.6.1 122 | 123 | 124 | 125 | com.alibaba 126 | fastjson 127 | 1.2.38 128 | 129 | 130 | 131 | org.springframework.mobile 132 | spring-mobile-device 133 | 134 | 135 | 136 | org.springframework.boot 137 | spring-boot-starter-test 138 | test 139 | 140 | 141 | 142 | 143 | 144 | 145 | org.springframework.boot 146 | spring-boot-maven-plugin 147 | 148 | 149 | 150 | org.mybatis.generator 151 | mybatis-generator-maven-plugin 152 | 1.3.5 153 | 154 | src/main/resources/generatorConfig.xml 155 | true 156 | true 157 | 158 | 159 | 160 | Generate MyBatis Artifacts 161 | 162 | generate 163 | 164 | 165 | 166 | 167 | 168 | org.mybatis.generator 169 | mybatis-generator-core 170 | 1.3.5 171 | 172 | 173 | mysql 174 | mysql-connector-java 175 | 5.1.38 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.controller; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.google.code.kaptcha.impl.DefaultKaptcha; 5 | import com.google.common.collect.ImmutableMap; 6 | import net.ameizi.shiro.LoginUser; 7 | import net.ameizi.shiro.jwt.JwtToken; 8 | import net.ameizi.utils.TokenUtil; 9 | import org.apache.shiro.SecurityUtils; 10 | import org.apache.shiro.authc.*; 11 | import org.apache.shiro.subject.Subject; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.data.redis.core.RedisTemplate; 14 | import org.springframework.mobile.device.Device; 15 | import org.apache.commons.codec.binary.Base64; 16 | import org.apache.commons.lang3.StringUtils; 17 | import org.springframework.web.bind.annotation.*; 18 | 19 | import javax.imageio.ImageIO; 20 | import javax.servlet.http.Cookie; 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | import java.awt.image.BufferedImage; 24 | import java.io.ByteArrayOutputStream; 25 | import java.io.IOException; 26 | import java.util.*; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | @RestController 30 | public class LoginController { 31 | 32 | @Autowired 33 | private TokenUtil tokenUtil; 34 | 35 | @Autowired 36 | private DefaultKaptcha captchaProducer; 37 | 38 | @Autowired 39 | private RedisTemplate redisTemplate; 40 | 41 | @GetMapping(value="/captcha") 42 | public Map captcha(){ 43 | try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 44 | String capText = captchaProducer.createText(); 45 | String uuid = UUID.randomUUID().toString(); 46 | redisTemplate.boundValueOps(uuid).set(capText,60, TimeUnit.SECONDS); 47 | BufferedImage bi = captchaProducer.createImage(capText); 48 | ImageIO.write(bi, "png", baos); 49 | String imgBase64 = Base64.encodeBase64String(baos.toByteArray()); 50 | return ImmutableMap.of(uuid,"data:image/jpeg;base64,"+imgBase64); 51 | } catch (IOException e) { 52 | throw new RuntimeException(e.getMessage(),e); 53 | } 54 | } 55 | 56 | @PostMapping("/login") 57 | public Object login(@RequestBody LoginUser loginUser, HttpServletRequest request, HttpServletResponse response, Device device) throws IOException { 58 | JSONObject jsonObject = new JSONObject(); 59 | 60 | String username = loginUser.getUsername(); 61 | String password = loginUser.getPassword(); 62 | 63 | // 验证用户名密码成功后生成token 64 | String token = tokenUtil.generateToken(username, device); 65 | // 构建JwtToken 66 | JwtToken jwtToken = JwtToken.builder().token(token).principal(username).build(); 67 | 68 | Subject subject = SecurityUtils.getSubject(); 69 | try { 70 | // 该方法会调用JwtRealm中的doGetAuthenticationInfo方法 71 | subject.login(jwtToken); 72 | } catch (UnknownAccountException exception) { 73 | exception.printStackTrace(); 74 | System.out.println("账号不存在"); 75 | } catch (IncorrectCredentialsException exception) { 76 | exception.printStackTrace(); 77 | System.out.println("错误的凭证,用户名或密码不正确"); 78 | } catch (LockedAccountException exception) { 79 | exception.printStackTrace(); 80 | System.out.println("账户已锁定"); 81 | } catch (ExcessiveAttemptsException exception) { 82 | exception.printStackTrace(); 83 | System.out.println("错误次数过多"); 84 | } catch (AuthenticationException exception) { 85 | exception.printStackTrace(); 86 | System.out.println("认证失败"); 87 | } 88 | 89 | // 认证通过 90 | if(subject.isAuthenticated()){ 91 | 92 | // 将token写出到cookie 93 | Cookie cookie =new Cookie("token",token); 94 | cookie.setHttpOnly(true); 95 | cookie.setMaxAge(3600 * 5); 96 | cookie.setPath("/"); 97 | response.addCookie(cookie); 98 | response.flushBuffer(); 99 | 100 | jsonObject.put("code",200); 101 | jsonObject.put("msg","success"); 102 | jsonObject.put("token",token); 103 | jsonObject.put("timestamp", Calendar.getInstance().getTimeInMillis()); 104 | return jsonObject; 105 | }else{ 106 | jsonObject.put("code",403); 107 | jsonObject.put("msg","error"); 108 | jsonObject.put("timestamp", Calendar.getInstance().getTimeInMillis()); 109 | return jsonObject; 110 | } 111 | } 112 | 113 | 114 | /** 115 | * 检查是否登录 116 | * @param token 117 | * @return 118 | */ 119 | @GetMapping(value = "/checkLogin") 120 | public Object checkLogin(@CookieValue("token") String token){ 121 | JSONObject jsonObject = new JSONObject(); 122 | jsonObject.put("code",200); 123 | if(StringUtils.isEmpty(token)){ 124 | jsonObject.put("msg","令牌为空"); 125 | } 126 | 127 | // 根据token获取用户信息 128 | 129 | // 检查token合法性及用户身份 130 | 131 | return jsonObject; 132 | } 133 | 134 | 135 | /** 136 | * 登出 137 | * @param request 138 | * @param response 139 | * @return 140 | * @throws IOException 141 | */ 142 | @GetMapping(value = "/logout") 143 | public Object logout(HttpServletRequest request,HttpServletResponse response) throws IOException { 144 | Optional cookie = Arrays.stream(request.getCookies()) 145 | .filter(ck -> "token".equals(ck.getName())) 146 | .limit(1) 147 | .map(ck -> { 148 | ck.setMaxAge(0); 149 | ck.setHttpOnly(true); 150 | ck.setPath("/"); 151 | return ck; 152 | }) 153 | .findFirst(); 154 | response.addCookie(cookie.get()); 155 | response.flushBuffer(); 156 | JSONObject jsonObject = new JSONObject(); 157 | jsonObject.put("code",200); 158 | jsonObject.put("msg","success"); 159 | return jsonObject; 160 | } 161 | 162 | 163 | /** 164 | * 更新token 165 | * @param token 166 | * @return 167 | */ 168 | @PostMapping("/token/refresh") 169 | public Object refreshToken(@CookieValue(value = "token") String token) { 170 | JSONObject jsonObject = new JSONObject(); 171 | String newToken = tokenUtil.refreshToken(token); 172 | jsonObject.put("code",200); 173 | jsonObject.put("msg","success"); 174 | jsonObject.put("token",newToken); 175 | jsonObject.put("timestamp", Calendar.getInstance().getTimeInMillis()); 176 | return jsonObject; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /src/main/resources/shiro.sql: -------------------------------------------------------------------------------- 1 | 2 | -- ---------------------------- 3 | -- Table structure for sys_resource 4 | -- ---------------------------- 5 | DROP TABLE IF EXISTS `sys_resource`; 6 | CREATE TABLE `sys_resource` ( 7 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 8 | `type` varchar(255) DEFAULT '' COMMENT '权限类型:menu、button、url', 9 | `name` varchar(255) NOT NULL COMMENT '权限名称', 10 | `permission` varchar(255) NOT NULL COMMENT '权限字符串', 11 | `icon` varchar(255) DEFAULT NULL, 12 | `sort` int(11) DEFAULT '0', 13 | `url` varchar(255) DEFAULT '', 14 | `description` varchar(255) DEFAULT '' COMMENT '资源描述', 15 | `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态值', 16 | `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '父ID', 17 | `create_by` bigint(20) DEFAULT NULL, 18 | `create_at` datetime NOT NULL, 19 | `update_by` bigint(20) DEFAULT NULL, 20 | `update_at` datetime DEFAULT NULL, 21 | PRIMARY KEY (`id`), 22 | UNIQUE KEY `uq_resource_type` (`type`,`permission`) USING BTREE, 23 | KEY `create_by` (`create_by`), 24 | KEY `update_by` (`update_by`) 25 | ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='资源(权限)表'; 26 | 27 | 28 | -- ---------------------------- 29 | -- Table structure for sys_role 30 | -- ---------------------------- 31 | DROP TABLE IF EXISTS `sys_role`; 32 | CREATE TABLE `sys_role` ( 33 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 34 | `name` varchar(255) NOT NULL, 35 | `code` varchar(255) NOT NULL, 36 | `remark` varchar(255) DEFAULT '', 37 | `status` tinyint(1) NOT NULL DEFAULT '1', 38 | `parent_id` bigint(20) DEFAULT NULL, 39 | `create_by` bigint(20) DEFAULT NULL, 40 | `create_at` datetime NOT NULL, 41 | `update_by` bigint(20) DEFAULT NULL, 42 | `update_at` datetime DEFAULT NULL, 43 | PRIMARY KEY (`id`), 44 | KEY `create_by` (`create_by`), 45 | KEY `update_by` (`update_by`) 46 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 47 | 48 | 49 | -- ---------------------------- 50 | -- Table structure for sys_role_resource 51 | -- ---------------------------- 52 | DROP TABLE IF EXISTS `sys_role_resource`; 53 | CREATE TABLE `sys_role_resource` ( 54 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 55 | `role_id` bigint(20) DEFAULT NULL, 56 | `resource_id` bigint(20) DEFAULT NULL, 57 | PRIMARY KEY (`id`), 58 | KEY `role_id` (`role_id`), 59 | KEY `resource_id` (`resource_id`) 60 | ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 61 | 62 | -- ---------------------------- 63 | -- Table structure for sys_user 64 | -- ---------------------------- 65 | DROP TABLE IF EXISTS `sys_user`; 66 | CREATE TABLE `sys_user` ( 67 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 68 | `mobile_phone` varchar(255) NOT NULL DEFAULT '' COMMENT '手机号码', 69 | `user_name` varchar(50) DEFAULT NULL COMMENT '用户名', 70 | `nickname` varchar(255) DEFAULT NULL COMMENT '昵称', 71 | `password` varchar(255) NOT NULL COMMENT '密码', 72 | `salt` varchar(255) DEFAULT '' COMMENT '加密混淆字符', 73 | `signature` varchar(255) DEFAULT NULL COMMENT '个性签名', 74 | `gender` tinyint(1) DEFAULT '0' COMMENT '性别', 75 | `qq` bigint(20) DEFAULT NULL COMMENT 'QQ号码', 76 | `email` varchar(255) DEFAULT NULL COMMENT '邮箱地址', 77 | `avatar` varchar(500) DEFAULT '' COMMENT '头像图片路径', 78 | `province` varchar(50) DEFAULT '' COMMENT '省', 79 | `city` varchar(50) DEFAULT '' COMMENT '市', 80 | `reg_ip` varchar(50) DEFAULT NULL COMMENT '注册时IP地址', 81 | `score` int(10) DEFAULT '0' COMMENT '积分值', 82 | `status` int(10) DEFAULT '1' COMMENT '状态:0禁用 1正常', 83 | `create_by` bigint(20) DEFAULT NULL, 84 | `create_at` datetime DEFAULT NULL, 85 | `update_by` bigint(20) DEFAULT NULL, 86 | `update_at` datetime DEFAULT NULL, 87 | PRIMARY KEY (`id`), 88 | UNIQUE KEY `uq_user_name` (`user_name`) 89 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='用户表'; 90 | 91 | -- ---------------------------- 92 | -- Table structure for sys_user_role 93 | -- ---------------------------- 94 | DROP TABLE IF EXISTS `sys_user_role`; 95 | CREATE TABLE `sys_user_role` ( 96 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 97 | `user_id` bigint(20) NOT NULL, 98 | `role_id` bigint(20) NOT NULL, 99 | PRIMARY KEY (`id`), 100 | KEY `fk_user_roles` (`user_id`), 101 | KEY `fk_role_users` (`role_id`) 102 | ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 103 | 104 | 105 | 106 | -- ---------------------------- 107 | -- Records of sys_user 108 | -- ---------------------------- 109 | INSERT INTO `sys_user` VALUES ('1', '18966668888', 'super', '超级管理员', 'e10adc3949ba59abbe56e057f20f883e', null, null, null, null, '', null, null, null, null, null, '1', null, '2015-09-28 17:47:18', null, '2015-09-30 17:36:16'); 110 | INSERT INTO `sys_user` VALUES ('2', '13988886666', 'admin', '系统管理员A', 'e10adc3949ba59abbe56e057f20f883e', null, null, null, '1234567', 'super@millinch.com', null, null, null, null, null, '1', null, '2015-09-29 17:47:22', null, '2015-09-30 17:32:07'); 111 | 112 | -- ---------------------------- 113 | -- Records of sys_role 114 | -- ---------------------------- 115 | INSERT INTO `sys_role` VALUES ('1', '超级管理员', 'superManager', '拥有所有权限', '1', null, '0', '2015-09-01 14:36:16', null, '2016-01-03 22:29:58'); 116 | INSERT INTO `sys_role` VALUES ('2', '系统管理员', 'systemManager', '拥有部分权限', '1', null, '0', '2015-08-30 18:03:47', null, '2015-08-30 18:03:47'); 117 | INSERT INTO `sys_role` VALUES ('3', '角色1', 'role1', 'nothing 34', '1', null, null, '2015-10-05 18:20:35', null, '2015-10-05 18:35:57'); 118 | 119 | -- ---------------------------- 120 | -- Records of sys_resource 121 | -- ---------------------------- 122 | INSERT INTO `sys_resource` VALUES ('1', '菜单', '系统管理', 'system:*', 'fa fa-dashboard', '1', '', '', '1', '0', '0', '2015-07-01 19:33:21', null, '2015-10-09 10:34:05'); 123 | INSERT INTO `sys_resource` VALUES ('2', '菜单', '角色管理', 'system:role:*', 'fa fa-male', '12', '/role/config', '', '1', '1', '0', '2015-07-01 19:38:38', null, '2015-07-01 19:38:38'); 124 | INSERT INTO `sys_resource` VALUES ('3', '菜单', '密码修改', 'system:password', null, '14', '/user/password/edition', '', '1', '1', '0', '2015-07-01 19:38:51', null, '2015-07-01 19:39:51'); 125 | INSERT INTO `sys_resource` VALUES ('4', '菜单', '操作日志', 'system:log:*', null, '15', '/handle/operation/log', '', '1', '1', '0', '2015-07-01 19:40:37', null, '2015-07-01 19:40:37'); 126 | INSERT INTO `sys_resource` VALUES ('5', 'URL', '新增角色', 'system:role:create', null, '13', '/role/addition', '', '1', '1', '0', '2015-07-01 19:41:21', null, '2015-10-08 16:45:43'); 127 | INSERT INTO `sys_resource` VALUES ('6', '菜单', '用户管理', 'system:admin:*', 'fa fa-users', '11', '/user/config', '', '1', '1', '0', '2015-07-01 19:34:38', null, '2015-07-01 19:34:38'); 128 | INSERT INTO `sys_resource` VALUES ('7', 'URL', '新增用户', 'system:admin:create', '', null, '/user/addition', 'bbb', '1', '0', '0', '2015-08-30 18:29:56', null, '2015-10-09 11:33:03'); 129 | 130 | -- ---------------------------- 131 | -- Records of sys_role_resource 132 | -- ---------------------------- 133 | INSERT INTO `sys_role_resource` VALUES ('1', '1', '1'); 134 | INSERT INTO `sys_role_resource` VALUES ('2', '1', '2'); 135 | INSERT INTO `sys_role_resource` VALUES ('3', '1', '3'); 136 | INSERT INTO `sys_role_resource` VALUES ('4', '1', '4'); 137 | INSERT INTO `sys_role_resource` VALUES ('5', '1', '5'); 138 | INSERT INTO `sys_role_resource` VALUES ('6', '1', '6'); 139 | INSERT INTO `sys_role_resource` VALUES ('7', '1', '7'); 140 | INSERT INTO `sys_role_resource` VALUES ('8', '2', '2'); 141 | 142 | -- ---------------------------- 143 | -- Records of sys_user_role 144 | -- ---------------------------- 145 | INSERT INTO `sys_user_role` VALUES ('1', '1', '1'); 146 | INSERT INTO `sys_user_role` VALUES ('2', '2', '2'); 147 | -------------------------------------------------------------------------------- /src/main/java/net/ameizi/config/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package net.ameizi.config; 2 | 3 | import net.ameizi.shiro.StatelessDefaultSubjectFactory; 4 | import net.ameizi.shiro.filter.JcaptchaValidateFilter; 5 | import net.ameizi.shiro.filter.JwtAuthenticationFilter; 6 | import net.ameizi.shiro.jwt.JwtRealm; 7 | import org.apache.shiro.SecurityUtils; 8 | import org.apache.shiro.authc.credential.CredentialsMatcher; 9 | import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 10 | import org.apache.shiro.cache.CacheManager; 11 | import org.apache.shiro.cache.MemoryConstrainedCacheManager; 12 | import org.apache.shiro.mgt.DefaultSecurityManager; 13 | import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; 14 | import org.apache.shiro.mgt.DefaultSubjectDAO; 15 | import org.apache.shiro.session.mgt.DefaultSessionManager; 16 | import org.apache.shiro.spring.LifecycleBeanPostProcessor; 17 | import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; 18 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 19 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 20 | import org.apache.shiro.web.mgt.DefaultWebSubjectFactory; 21 | import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; 22 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | import org.springframework.context.annotation.DependsOn; 26 | import org.springframework.web.filter.DelegatingFilterProxy; 27 | 28 | import javax.servlet.Filter; 29 | import java.util.LinkedHashMap; 30 | import java.util.Map; 31 | 32 | @Configuration 33 | public class ShiroConfig { 34 | 35 | /** 36 | * 自定义Realm 37 | * @return 38 | */ 39 | @Bean(name = "jwtRealm") 40 | @DependsOn("lifecycleBeanPostProcessor") 41 | public JwtRealm jwtRealm() { 42 | JwtRealm jwtRealm = new JwtRealm(); 43 | // jwtRealm.setCredentialsMatcher(credentialsMatcher()); 44 | jwtRealm.setCachingEnabled(false); 45 | return jwtRealm; 46 | } 47 | 48 | @Bean 49 | public FilterRegistrationBean delegatingFilterProxy() { 50 | FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); 51 | DelegatingFilterProxy proxy = new DelegatingFilterProxy(); 52 | proxy.setTargetFilterLifecycle(true); 53 | proxy.setTargetBeanName("shiroFilter"); 54 | filterRegistrationBean.setFilter(proxy); 55 | return filterRegistrationBean; 56 | } 57 | 58 | @Bean("shiroFilter") 59 | @DependsOn("securityManager") 60 | public ShiroFilterFactoryBean shiroFilter(DefaultSecurityManager securityManager){ 61 | ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); 62 | shiroFilter.setSecurityManager(securityManager); 63 | // 拦截器 64 | Map filterChainDefinitionMap = new LinkedHashMap(); 65 | 66 | // 允许用户匿名访问/login(登录接口) 67 | filterChainDefinitionMap.put("/login", "anon"); 68 | 69 | // 验证码允许匿名访问 70 | filterChainDefinitionMap.put("/captcha","anon"); 71 | filterChainDefinitionMap.put("/api-docs","anon"); 72 | filterChainDefinitionMap.put("/v2/api-docs","anon"); 73 | filterChainDefinitionMap.put("/swagger-ui.html","anon"); 74 | filterChainDefinitionMap.put("/webjars/**","anon"); 75 | filterChainDefinitionMap.put("/swagger-resources/**","anon"); 76 | 77 | filterChainDefinitionMap.put("/**", "jwt"); 78 | 79 | // filterChainDefinitionMap.put("/**","anon"); 80 | 81 | shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap); 82 | 83 | Map filters = new LinkedHashMap<>(); 84 | filters.put("jwt",new JwtAuthenticationFilter()); 85 | filters.put("jcaptchaValidate",new JcaptchaValidateFilter()); 86 | 87 | shiroFilter.setFilters(filters); 88 | 89 | return shiroFilter; 90 | } 91 | 92 | /** 93 | * Subject工厂管理器 94 | * @return 95 | */ 96 | @Bean 97 | public DefaultWebSubjectFactory subjectFactory(){ 98 | DefaultWebSubjectFactory subjectFactory = new StatelessDefaultSubjectFactory(); 99 | return subjectFactory; 100 | } 101 | 102 | /** 103 | * 安全管理器 104 | * @return 105 | */ 106 | @Bean("securityManager") 107 | public DefaultWebSecurityManager securityManager(){ 108 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); 109 | 110 | securityManager.setRealm(jwtRealm()); 111 | 112 | // 替换默认的DefaultSubjectFactory,用于关闭session功能 113 | securityManager.setSubjectFactory(subjectFactory()); 114 | securityManager.setSessionManager(sessionManager()); 115 | 116 | // 关闭session存储,禁用Session作为存储策略的实现,但它没有完全地禁用Session所以需要配合SubjectFactory中的context.setSessionCreationEnabled(false) 117 | ((DefaultSessionStorageEvaluator) ((DefaultSubjectDAO)securityManager.getSubjectDAO()).getSessionStorageEvaluator()).setSessionStorageEnabled(false); 118 | 119 | // 用户授权/认证信息Cache, 后期可采用EhCache缓存 120 | // securityManager.setCacheManager(cacheManager()); 121 | 122 | SecurityUtils.setSecurityManager(securityManager); 123 | return securityManager; 124 | } 125 | 126 | /** 127 | * 会话管理器 128 | * @return 129 | */ 130 | public DefaultSessionManager sessionManager(){ 131 | DefaultSessionManager sessionManager =new DefaultSessionManager(); 132 | // 关闭session定时检查,通过setSessionValidationSchedulerEnabled禁用掉会话调度器 133 | sessionManager.setSessionValidationSchedulerEnabled(false); 134 | return sessionManager; 135 | } 136 | 137 | /** 138 | * 用户授权信息缓存 139 | * @return 140 | */ 141 | @Bean 142 | public CacheManager cacheManager() { 143 | // EhCacheManager cacheManager = new EhCacheManager(); 144 | // cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml"); 145 | return new MemoryConstrainedCacheManager(); 146 | } 147 | 148 | /** 149 | * 凭证匹配器 150 | * @return 151 | */ 152 | @Bean 153 | public CredentialsMatcher credentialsMatcher(){ 154 | HashedCredentialsMatcher hashedCredentialsMatcher =new HashedCredentialsMatcher(); 155 | hashedCredentialsMatcher.setHashAlgorithmName("MD5"); 156 | hashedCredentialsMatcher.setHashIterations(1024); 157 | hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); 158 | return hashedCredentialsMatcher; 159 | } 160 | 161 | /** 162 | * Shiro生命周期处理器 163 | * @return 164 | */ 165 | @Bean(name = "lifecycleBeanPostProcessor") 166 | public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { 167 | return new LifecycleBeanPostProcessor(); 168 | } 169 | 170 | /** 171 | * 开启Shiro注解(如@RequiresRoles,@RequiresPermissions) 172 | * @return 173 | */ 174 | @Bean 175 | @DependsOn("lifecycleBeanPostProcessor") 176 | public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ 177 | DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); 178 | advisorAutoProxyCreator.setProxyTargetClass(true); 179 | return advisorAutoProxyCreator; 180 | } 181 | 182 | @Bean 183 | public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){ 184 | AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); 185 | authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); 186 | return authorizationAttributeSourceAdvisor; 187 | } 188 | } 189 | --------------------------------------------------------------------------------