优化首页

This commit is contained in:
zhujj 2019-01-08 17:17:54 +08:00
parent 4516c36b1d
commit f371c83579
26 changed files with 1090 additions and 211 deletions

View File

@ -12,7 +12,7 @@ public class AliyunOSSRest {
// @Autowired
// private AliyunOSSClient aliyunOSSClient ;
@GetMapping("policy.json")
public JSONObject getPolicy() {
public JSONObject getPolicy(String id) {
String dir = String.valueOf(System.currentTimeMillis());
// return aliyunOSSClient .getPostObjectPolicy(dir);
return null;

View File

@ -25,9 +25,9 @@ import io.swagger.annotations.ApiOperation;
@Api("用户信息管理")
@RestController
@RequestMapping("/test/*")
public class TestController extends BaseController
{
public class TestController extends BaseController {
private final static List<Test> testList = new ArrayList<>();
{
testList.add(new Test("1", "admin", "admin123"));
testList.add(new Test("2", "ry", "admin123"));
@ -35,62 +35,52 @@ public class TestController extends BaseController
@ApiOperation("获取列表")
@GetMapping("list")
public List<Test> testList()
{
public List<Test> testList() {
return testList;
}
@ApiOperation("新增用户")
@PostMapping("save")
public AjaxResult save(Test test)
{
public AjaxResult save(Test test) {
return testList.add(test) ? success() : error();
}
@ApiOperation("更新用户")
@ApiImplicitParam(name = "Test", value = "单个用户信息", dataType = "Test")
@PutMapping("update")
public AjaxResult update(Test test)
{
public AjaxResult update(Test test) {
return testList.remove(test) && testList.add(test) ? success() : error();
}
@ApiOperation("删除用户")
@ApiImplicitParam(name = "Tests", value = "单个用户信息", dataType = "Test")
@DeleteMapping("delete")
public AjaxResult delete(Test test)
{
public AjaxResult delete(Test test) {
return testList.remove(test) ? success() : error();
}
}
class Test
{
class Test {
private String userId;
private String username;
private String password;
public Test()
{
public Test() {
}
public Test(String userId, String username, String password)
{
public Test(String userId, String username, String password) {
this.userId = userId;
this.username = username;
this.password = password;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass())
{
if (o == null || getClass() != o.getClass()) {
return false;
}
@ -100,41 +90,34 @@ class Test
}
@Override
public int hashCode()
{
public int hashCode() {
int result = userId != null ? userId.hashCode() : 0;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
public String getUserId()
{
public String getUserId() {
return userId;
}
public void setUserId(String userId)
{
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername()
{
public String getUsername() {
return username;
}
public void setUsername(String username)
{
public void setUsername(String username) {
this.username = username;
}
public String getPassword()
{
public String getPassword() {
return password;
}
public void setPassword(String password)
{
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -32,7 +32,7 @@ public class SwaggerConfig
.apiInfo(apiInfo())
.select()
// 指定当前包路径
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.web.controller.tool"))
.apis(RequestHandlerSelectors.basePackage("com.ruoyi"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();

View File

@ -75,8 +75,10 @@
height: 1px;
}
.step-content{
margin-top: 20px;
}
.step-list{
display: none;
clear: both;
margin-bottom: 30px;
}

View File

@ -30,7 +30,8 @@ function login() {
"rememberMe": rememberMe
},
success: function(r) {
if (r.code == 0) {
debugger
if (r.code == 200) {
location.href = ctx + 'index';
} else {
$.modal.closeLoading();

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.base;
import com.ruoyi.common.constant.Constants;
import java.util.HashMap;
/**
@ -64,7 +66,7 @@ public class AjaxResult extends HashMap<String, Object>
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("code", 0);
json.put("code", Constants.SUCCESS);
return json;
}

View File

@ -15,12 +15,12 @@ public class Constants
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
public static final String SUCCESS = "200";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
public static final String FAIL = "500";
/**
* 登录成功

View File

@ -139,6 +139,12 @@
<version>3.3.0</version>
<type>pom</type>
</dependency>
<!--前后端分离验证-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,31 @@
package com.ruoyi.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 跨域设置
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 1允许任何域名使用
corsConfiguration.addAllowedOrigin("*");
// 2允许任何头
corsConfiguration.addAllowedHeader("*");
// 3允许任何方法postget等
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}

View File

@ -246,6 +246,7 @@ public class ShiroConfig
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/api/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
// 退出 logout地址shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");

View File

@ -0,0 +1,72 @@
package com.ruoyi.framework.jwt;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Mr.Li
* @create 2018-07-12 15:56
* @desc
**/
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
* 执行登录认证
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
return false;
}
}
/**
*
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader("Authorization");
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入如果错误他会抛出异常并被捕获
getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功返回true
return true;
}
/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
// 跨域时会首先发送一个option请求这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
}

View File

@ -0,0 +1,27 @@
package com.ruoyi.framework.jwt;
import org.apache.shiro.authc.AuthenticationToken;
/**
* @author Mr.Li
* @create 2018-07-12 15:19
* @desc
**/
public class JwtToken implements AuthenticationToken {
private String token;
public JwtToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}

View File

@ -0,0 +1,75 @@
package com.ruoyi.framework.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.ruoyi.framework.web.util.ServletUtils;
import java.util.Date;
/**
* @author Mr.Li
* @create 2018-07-12 14:23
* @desc JWT工具类
**/
public class JwtUtil {
private static final long EXPIRE_TIME = 5 * 60 * 1000;
/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
//根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
//效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getLoginName() {
try {
String token = ServletUtils.getRequest().getHeader("token");
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("loginName").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min后过期
*
* @param loginName 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String loginName, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
.withClaim("loginName", loginName)
.withExpiresAt(date)
.sign(algorithm);
}
}

View File

@ -2,6 +2,8 @@ package com.ruoyi.framework.shiro.realm;
import java.util.HashSet;
import java.util.Set;
import com.ruoyi.framework.jwt.JwtToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;

View File

@ -65,6 +65,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="userType != null and userType != ''">
AND u.user_type = #{userType}
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>

View File

@ -22,15 +22,14 @@ import com.ruoyi.common.utils.ExcelUtil;
* @author zhujj
* @date 2018-11-29
*/
@Controller
@RequestMapping("/agile/genTable")
@RestController
@RequestMapping("/genTable")
public class GenTableController extends BaseController {
private String prefix = "agile/genTable";
@Autowired
private IGenTableService genTableService;
@RequiresPermissions("agile:genTable:view")
@GetMapping()
public String genTable() {
return prefix + "/genTable";
@ -39,7 +38,6 @@ public class GenTableController extends BaseController {
/**
* 查询代码生成列表
*/
@RequiresPermissions("agile:genTable:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(GenTable genTable) {
@ -51,7 +49,6 @@ public class GenTableController extends BaseController {
/**
* 导出代码生成列表
*/
@RequiresPermissions("agile:genTable:export")
@PostMapping("/export")
@ResponseBody
public AjaxResult export(GenTable genTable) {
@ -71,7 +68,6 @@ public class GenTableController extends BaseController {
/**
* 新增保存代码生成
*/
@RequiresPermissions("agile:genTable:add")
@Log(title = "代码生成", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
@ -93,7 +89,6 @@ public class GenTableController extends BaseController {
/**
* 修改保存代码生成
*/
@RequiresPermissions("agile:genTable:edit")
@Log(title = "代码生成", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@ResponseBody
@ -104,7 +99,6 @@ public class GenTableController extends BaseController {
/**
* 删除代码生成
*/
@RequiresPermissions("agile:genTable:remove")
@Log(title = "代码生成", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody

View File

@ -2,46 +2,58 @@
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<meta charset="utf-8">
<head th:include="include :: header"></head>
<link th:href="@{/ajax/libs/layui/extend/steps/steps.css}" rel="stylesheet"/>
<link th:href="@{/ajax/libs/layui/css/layui.css}" rel="stylesheet"/>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-genTable-add">
<div id="step_demo" class="step-body">
<div class="step-header" style="width:80%;overflow: hidden;">
<ul>
<li>
<span class="step-name">选择表</span>
</li>
<li>
<span class="step-name">基本信息</span>
</li>
<li>
<span class="step-name">字段信息</span>
</li>
<li>
<span class="step-name">生成信息</span>
</li>
<li>
<span class="step-name">完成</span>
</li>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
<legend>基本信息</legend>
</fieldset>
<form class="form-horizontal m">
<div class="layui-inline">
<label class="layui-form-label">搜索选择框</label>
<div class="layui-input-inline">
<select name="modules" lay-verify="required" lay-search="">
<option value="">直接选择或搜索选择</option>
<option value="1">layer</option>
<option value="2">form</option>
<option value="3">layim</option>
<option value="4">element</option>
<option value="5">laytpl</option>
<option value="6">upload</option>
<option value="7">laydate</option>
<option value="8">laypage</option>
<option value="9">flow</option>
<option value="10">util</option>
<option value="11">code</option>
<option value="12">tree</option>
<option value="13">layedit</option>
<option value="14">nav</option>
<option value="15">tab</option>
<option value="16">table</option>
<option value="17">select</option>
<option value="18">checkbox</option>
<option value="19">switch</option>
<option value="20">radio</option>
</select>
</div>
</div>
</form>
</div>
<div class="wrapper wrapper-content animated fadeInRight ibox-content hide">
<div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
<ul class="layui-tab-title">
<li class="layui-this">基本信息</li>
<li>字段信息</li>
<li>生成信息</li>
</ul>
</div>
<div class="step-content">
<div class="step-list">
<div class="form-group">
<label class="col-sm-3 control-label">数据源名称:</label>
<div class="col-sm-8">
<input id="dataSourceName" name="dataSourceName" class="form-control" type="text">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">选择表:</label>
<div class="col-sm-8">
<input id="tableName" name="tableName" class="form-control" type="text">
</div>
</div>
</div>
<div class="step-list">
<div class="layui-tab-content" style="height: 100px;">
<form class="form-horizontal m" id="form-genTable-add">
<div class="layui-tab-item layui-show">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
<legend>基本信息</legend>
</fieldset>
<div>
<div class="form-group">
<label class="col-sm-3 control-label">实体类名称:</label>
<div class="col-sm-8">
@ -73,8 +85,16 @@
</div>
</div>
</div>
<div class="step-list">待确认</div>
<div class="step-list">
</div>
<div class="layui-tab-item">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
<legend>字段信息</legend>
</fieldset>
</div>
<div class="layui-tab-item">
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 50px;">
<legend>生成信息</legend>
</fieldset>
<div class="form-group">
<label class="col-sm-3 control-label">使用的模板:</label>
<div class="col-sm-8">
@ -130,14 +150,14 @@
</div>
</div>
</div>
</div>
</div>
</form>
<button id="preBtn">上一步</button>
<button id="nextBtn">下一步</button>
</div>
</div>
</div>
<div th:include="include::footer"></div>
<script th:src="@{/ajax/libs/layui/layui.js}"></script>
<script type="text/javascript">
var prefix = ctx + "agile/genTable"
$("#form-genTable-add").validate({
@ -153,33 +173,48 @@
$.operate.save(prefix + "/add", $('#form-genTable-add').serialize());
}
}
layui.config({
debug: true
,base: '/ajax/libs/layui/extend/'
}).extend({
steps:"steps/steps"
layui.use(['element','form', 'layedit', 'laydate'], function(){
var $ = layui.jquery
,element = layui.element; //Tab的切换功能切换事件监听等需要依赖element模块
//触发事件
var active = {
tabAdd: function(){
//新增一个Tab项
element.tabAdd('demo', {
title: '新选项'+ (Math.random()*1000|0) //用于演示
,content: '内容'+ (Math.random()*1000|0)
,id: new Date().getTime() //实际使用一般是规定好的id这里以时间戳模拟下
})
}
,tabDelete: function(othis){
//删除指定Tab项
element.tabDelete('demo', '44'); //删除:“商品管理”
othis.addClass('layui-btn-disabled');
}
,tabChange: function(){
//切换到指定Tab项
element.tabChange('demo', '22'); //切换到:用户管理
}
};
$('.site-demo-active').on('click', function(){
var othis = $(this), type = othis.data('type');
active[type] ? active[type].call(this, othis) : '';
});
//Hash地址的定位
var layid = location.hash.replace(/^#test=/, '');
element.tabChange('test', layid);
layui.use(['jquery', 'steps'],function(){
var $ = layui.$;
var $step= $("#step_demo").step();
$("#preBtn").click(function(event) {
console.log("上一步");
$step.preStep();//上一步
});
$("#nextBtn").click(function(event) {
console.log("下一步");
$step.nextStep();//下一步
});
$("#goBtn").click(function(event) {
$step.goStep(3);//到指定步
element.on('tab(test)', function(elem){
location.hash = 'test='+ $(this).attr('lay-id');
});
});
</script>
</body>
</html>

View File

@ -4,6 +4,7 @@
<head th:include="include :: header"></head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
@ -26,7 +27,7 @@
</div>
<div class="btn-group-sm hidden-xs" id="toolbar" role="group">
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="agile:genTable:add">
<a class="btn btn-success" onclick="$.operate.addFull()" shiro:hasPermission="agile:genTable:add">
<i class="fa fa-plus"></i> 添加
</a>
<a class="btn btn-primary btn-edit disabled" onclick="$.operate.edit()" shiro:hasPermission="agile:genTable:edit">

View File

@ -25,6 +25,11 @@
<artifactId>ruoyi-framework</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.19</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,178 @@
package com.ruoyi.vip.controller;
import com.auth0.jwt.JWTVerifier;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.base.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.json.JSONObject;
import com.ruoyi.common.utils.ExcelUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.jwt.JwtUtil;
import com.ruoyi.framework.shiro.service.SysPasswordService;
import com.ruoyi.framework.web.base.BaseController;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.framework.web.util.ServletUtils;
import com.ruoyi.framework.web.util.ShiroUtils;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.vip.domain.VipUser;
import com.ruoyi.vip.service.IVipUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户信息
*
* @author ruoyi
*/
@Api("用户信息管理")
@RestController
@RequestMapping("/api/v1/vip/user")
public class ApiVipUserController extends BaseController {
@Autowired
private IVipUserService userService;
@Autowired
private SysPasswordService passwordService;
@ApiOperation("用户登陆")
@Log(title = "用户登陆", businessType = BusinessType.EXPORT)
@RequestMapping(value = "/login", method = RequestMethod.POST,produces= "application/json;charset=UTF-8")
public AjaxResult login(@RequestBody SysUser user) {
AjaxResult success = success("登陆成功");
boolean rememberMe=false;
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword(), rememberMe);
Subject subject = SecurityUtils.getSubject();
try
{
subject.login(token);
String tokenSign=JwtUtil.sign(user.getUserName(),user.getUserName());
JSONObject json = new JSONObject();
json.put("token",tokenSign);
success.put("data",json);
return success;
}
catch (AuthenticationException e)
{
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(e.getMessage()))
{
msg = e.getMessage();
}
return error(msg);
}
}
@GetMapping("/info")
public AjaxResult get() {
AjaxResult success = success("登陆成功");
VipUser vipUser = userService.selectUserByLoginName(JwtUtil.getLoginName());
success.put("user", vipUser);
SysUser vipUser2 =ShiroUtils.getSysUser();
return success;
}
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(VipUser user) {
List<VipUser> list = userService.selectUserList( user );
return getDataTable( list );
}
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(VipUser user) {
List<VipUser> list = userService.selectUserList( user );
ExcelUtil<VipUser> util = new ExcelUtil<VipUser>( VipUser.class );
return util.exportExcel( list, "user" );
}
/**
* 新增保存用户
*/
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@Transactional(rollbackFor = Exception.class)
@ResponseBody
public AjaxResult addSave(VipUser user) {
user.setSalt( ShiroUtils.randomSalt() );
user.setPassword( passwordService.encryptPassword( user.getLoginName(), user.getPassword(), user.getSalt() ) );
user.setCreateBy( ShiroUtils.getLoginName() );
return toAjax( userService.insertUser( user ) );
}
/**
* 修改保存用户
*/
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
@Transactional(rollbackFor = Exception.class)
@ResponseBody
public AjaxResult editSave(VipUser user) {
user.setUpdateBy( ShiroUtils.getLoginName() );
return toAjax( userService.updateUser( user ) );
}
@Log(title = "重置密码", businessType = BusinessType.UPDATE)
@PostMapping("/resetPwd")
@ResponseBody
public AjaxResult resetPwdSave(VipUser user) {
user.setSalt( ShiroUtils.randomSalt() );
user.setPassword( passwordService.encryptPassword( user.getLoginName(), user.getPassword(), user.getSalt() ) );
return toAjax( userService.resetUserPwd( user ) );
}
@RequiresPermissions("vip:user:remove")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids) {
try {
return toAjax( userService.deleteUserByIds( ids ) );
} catch (Exception e) {
return error( e.getMessage() );
}
}
/**
* 校验用户名
*/
@PostMapping("/checkLoginNameUnique")
@ResponseBody
public String checkLoginNameUnique(VipUser user) {
return userService.checkLoginNameUnique( user.getLoginName() );
}
/**
* 校验手机号码
*/
@PostMapping("/checkPhoneUnique")
@ResponseBody
public String checkPhoneUnique(VipUser user) {
return userService.checkPhoneUnique( user );
}
/**
* 校验email邮箱
*/
@PostMapping("/checkEmailUnique")
@ResponseBody
public String checkEmailUnique(VipUser user) {
return userService.checkEmailUnique( user );
}
}

View File

@ -12,7 +12,7 @@ import java.util.Date;
import java.util.List;
/**
* 用户对象 sys_user
* 用户对象 vip_user
*
* @author ruoyi
*/

View File

@ -0,0 +1,32 @@
package com.ruoyi.vip.framework;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* 自定义带用户类型token
* @author Sunny
*/
public class UsernamePasswordByUserTypeToken extends UsernamePasswordToken {
private static final long serialVersionUID = -7638434498222500528L;
/*
* 用户类型
* 1:积分后台用户(后台管理员)
* 2:积分兑换端用户(店长店员)
* 3:积分验证端用户(第三方合作店铺)
*/
private String userType;
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public UsernamePasswordByUserTypeToken(String username, String password, String userType) {
super(username, password);
this.userType = userType;
}
}

View File

@ -0,0 +1,134 @@
package com.ruoyi.vip.framework;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.shiro.service.SysPasswordService;
import com.ruoyi.framework.web.exception.user.*;
import com.ruoyi.framework.web.util.MessageUtils;
import com.ruoyi.framework.web.util.ServletUtils;
import com.ruoyi.framework.web.util.ShiroUtils;
import com.ruoyi.vip.domain.VipUser;
import com.ruoyi.vip.service.IVipUserService;
import org.apache.shiro.authc.credential.PasswordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* 登录校验方法
*
* @author ruoyi
*/
@Component
public class VipLoginService
{
@Autowired
private VipPasswordService vipPasswordService;
@Autowired
private IVipUserService vipUserService;
/**
* 登录
*/
public VipUser login(String username, String password)
{
// 验证码校验
if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
throw new UserNotExistsException();
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// 查询用户信息
VipUser user = vipUserService.selectUserByLoginName(username);
if (user == null && maybeMobilePhoneNumber(username))
{
user = vipUserService.selectUserByPhoneNumber(username);
}
if (user == null && maybeEmail(username))
{
user = vipUserService.selectUserByEmail(username);
}
if (user == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
throw new UserNotExistsException();
}
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));
throw new UserDeleteException();
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));
throw new UserBlockedException(user.getRemark());
}
vipPasswordService.validate(user, password);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
recordLoginInfo(user);
return user;
}
private boolean maybeEmail(String username)
{
if (!username.matches(UserConstants.EMAIL_PATTERN))
{
return false;
}
return true;
}
private boolean maybeMobilePhoneNumber(String username)
{
if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN))
{
return false;
}
return true;
}
/**
* 记录登录信息
*/
public void recordLoginInfo(VipUser user)
{
user.setLoginIp(ShiroUtils.getIp());
user.setLoginDate(DateUtils.getNowDate());
vipUserService.updateUserInfo(user);
}
}

View File

@ -0,0 +1,87 @@
package com.ruoyi.vip.framework;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.exception.user.UserPasswordNotMatchException;
import com.ruoyi.framework.web.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.framework.web.util.MessageUtils;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.vip.domain.VipUser;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 登录密码方法
*
* @author ruoyi
*/
@Component
public class VipPasswordService
{
@Autowired
private CacheManager cacheManager;
private Cache<String, AtomicInteger> loginRecordCache;
@Value(value = "${user.password.maxRetryCount}")
private String maxRetryCount;
@PostConstruct
public void init()
{
loginRecordCache = cacheManager.getCache("loginRecordCache");
}
public void validate(VipUser user, String password)
{
String loginName = user.getLoginName();
AtomicInteger retryCount = loginRecordCache.get(loginName);
if (retryCount == null)
{
retryCount = new AtomicInteger(0);
loginRecordCache.put(loginName, retryCount);
}
if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount)));
throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());
}
if (!matches(user, password))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount)));
loginRecordCache.put(loginName, retryCount);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(loginName);
}
}
public boolean matches(VipUser user, String newPassword)
{
return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
}
public void clearLoginRecordCache(String username)
{
loginRecordCache.remove(username);
}
public String encryptPassword(String username, String password, String salt)
{
return new Md5Hash(username + password + salt).toHex().toString();
}
}

View File

@ -0,0 +1,112 @@
package com.ruoyi.vip.framework;
import com.ruoyi.framework.shiro.service.SysLoginService;
import com.ruoyi.framework.web.exception.user.*;
import com.ruoyi.framework.web.util.ShiroUtils;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.vip.domain.VipUser;
import com.ruoyi.vip.utils.VipUserUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.Set;
/**
* 自定义Realm 处理登录 权限
*
* @author ruoyi
*/
public class VipUserRealm extends AuthorizingRealm
{
private static final Logger log = LoggerFactory.getLogger(VipUserRealm.class);
@Autowired
private VipLoginService vipLoginService;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
{
VipUser user = VipUserUtils.getVipUser();
// 角色列表
Set<String> roles = new HashSet<String>();
// 功能列表
Set<String> menus = new HashSet<String>();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
UsernamePasswordByUserTypeToken upToken = (UsernamePasswordByUserTypeToken) token;
String username = upToken.getUsername();
String password = "";
if (upToken.getPassword() != null)
{
password = new String(upToken.getPassword());
}
VipUser user = null;
try
{
user = vipLoginService.login(username, password);
}
catch (CaptchaException e)
{
throw new AuthenticationException(e.getMessage(), e);
}
catch (UserNotExistsException e)
{
throw new UnknownAccountException(e.getMessage(), e);
}
catch (UserPasswordNotMatchException e)
{
throw new IncorrectCredentialsException(e.getMessage(), e);
}
catch (UserPasswordRetryLimitExceedException e)
{
throw new ExcessiveAttemptsException(e.getMessage(), e);
}
catch (UserBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (RoleBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (Exception e)
{
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
/**
* 清理缓存权限
*/
public void clearCachedAuthorizationInfo()
{
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.vip.utils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.vip.domain.VipUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
/**
* shiro 工具类
*
* @author ruoyi
*/
public class VipUserUtils
{
public static Subject getSubjct()
{
return SecurityUtils.getSubject();
}
public static Session getSession()
{
return SecurityUtils.getSubject().getSession();
}
public static void logout()
{
getSubjct().logout();
}
public static VipUser getVipUser()
{
VipUser user = null;
Object obj = getSubjct().getPrincipal();
if (StringUtils.isNotNull(obj))
{
user = new VipUser();
BeanUtils.copyBeanProp(user, obj);
}
return user;
}
public static void setVipUser(VipUser user)
{
Subject subject = getSubjct();
PrincipalCollection principalCollection = subject.getPrincipals();
String realmName = principalCollection.getRealmNames().iterator().next();
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
// 重新加载Principal
subject.runAs(newPrincipalCollection);
}
public static void clearCachedAuthorizationInfo()
{
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
UserRealm realm = (UserRealm) rsm.getRealms().iterator().next();
realm.clearCachedAuthorizationInfo();
}
public static Long getUserId()
{
return getVipUser().getId().longValue();
}
public static String getLoginName()
{
return getVipUser().getLoginName();
}
public static String getIp()
{
return getSubjct().getSession().getHost();
}
public static String getSessionId()
{
return String.valueOf(getSubjct().getSession().getId());
}
/**
* 生成随机盐
*/
public static String randomSalt()
{
// 一个Byte占两个字节此处生成的3字节字符串长度为6
SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
String hex = secureRandom.nextBytes(3).toHex();
return hex;
}
}