登录会话是基本操作,不管哪个应用都会用到的模块,针对于此,个人简笔编写了一个简单案例,针对前面spring security的功能文章进行完善
操作链接:用户中心-数据库设计
操作链接:springboot植入pagerHelper和spring mybatis更新几种操作
这里制作简单的用户名和密码操作,查询用户资源路径,通过用户电话号码查询用户信息,匹配密码,具体代码如下:
/**
* 电话号码登录
* @param phone 用户电话号码
* @param password 加密后密码
* @return
*/
@Override
public UserData login(String phone, @NotNull String password) {
Example example = new Example(User.class);
example.createCriteria().andEqualTo("userPhone", phone);
User user = userMapper.selectOneByExample(example);
if (user != null && password.equals(user.getUserPassword())) {
UserData userData = new UserData();
/**
* 查询用户资源信息
*/
userData.setUserResources(resourceMapper.selectResourceByRoleId(user.getId()));
BeanUtils.copyProperties(user, userData);
Logon logon = new Logon();
logon.setToken(UUID.randomUUID().toString().replaceAll("-", ""));
logon.setUserId(user.getId());
try {
logon.setResourceData(objectMapper.writeValueAsString(userData.getUserResources()));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
Example logonExample = new Example(Logon.class);
logonExample.createCriteria().andEqualTo("userId", user.getId());
logonMapper.deleteByExample(logonExample);
logonMapper.insert(logon);
userData.setToken(logon.getToken());
return userData;
}
return null;
}
查询用户资源的sql配置
<select id="selectResourceByRoleId" resultMap="BaseResultMap">
SELECT `tb_resource`.`id`,
`resource_code`,
`resource_url`,
`resource_description`,
`resource_state`
FROM `tb_resource`
RIGHT JOIN `role_resource`
ON `tb_resource`.`id` = `role_resource`.`resource_id`
RIGHT JOIN `user_role`
ON `user_role`.`role_id` = `role_resource`.`role_id`
WHERE `user_role`.`user_id` = #{userId}
select>
具体登录的controller代码
package com.lgh.controller;
import com.lgh.common.result.CommonResult;
import com.lgh.common.result.inter.IResult;
import com.lgh.model.domain.UserData;
import com.lgh.service.ILogonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
@Api(tags = "登录服务")
@RestController
@RequestMapping("/login")
@Validated
public class LogonController {
@Autowired
private ILogonService logonService;
@ApiOperation("登录服务接口")
@PostMapping("/sign")
@Valid
public IResult<UserData> login(@NotNull @RequestParam("phone") String phone, @NotNull @RequestParam("password") String password) {
UserData userData = logonService.login(phone, password);
return CommonResult.successData(userData);
}
}
通过spring security的过滤器进行拦截认证,通过注解@RolesAllowed授权。由于spring security的filter不能给spring管理,否则会全局拦截,因此这里要获取请求接口,的拿到service的bean,所以先获取application的类。
package com.lgh.common.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> t) {
return applicationContext.getBean(t);
}
}
由于jsr250会添加前缀,个人不太喜欢前缀的ROLE_,因此我实现GrantedAuthority时也默认给前缀,如下代码
package com.lgh.common.authority.authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
public class MySimpleGrantedAuthority implements GrantedAuthority {
private static final long serialVersionUID = 510L;
private final String rolePrefix = "ROLE_";
private final String role;
public MySimpleGrantedAuthority(String role) {
Assert.hasText(role, "A granted authority textual representation is required");
this.role = role;
}
public String getAuthority() {
return rolePrefix + this.role;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof MySimpleGrantedAuthority ? this.role.equals(((MySimpleGrantedAuthority) obj).role) : false;
}
}
public int hashCode() {
return this.role.hashCode();
}
public String toString() {
return this.role;
}
}
下面我们来编写实际的过滤器
package com.lgh.common.authority.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lgh.common.authority.authentication.MyAuthentication;
import com.lgh.common.authority.authentication.MySimpleGrantedAuthority;
import com.lgh.common.authority.entity.UserDetail;
import com.lgh.common.result.CommonResult;
import com.lgh.common.util.ApplicationUtil;
import com.lgh.model.Resource;
import com.lgh.model.domain.UserData;
import com.lgh.service.ILogonService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* spring security过滤器,不要交给spring 管理
*/
public class MyAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token");
if (StringUtils.isEmpty(token)) {
filterChain.doFilter(request, response);
} else {
ILogonService logonService = ApplicationUtil.getBean(ILogonService.class);
UserData userData = logonService.verify(token);
if (userData == null) {
ObjectMapper objectMapper = ApplicationUtil.getBean(ObjectMapper.class);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(objectMapper.writeValueAsString(CommonResult.deny()));
return;
} else {
UserDetail userDetail = new UserDetail();
userDetail.setId(userData.getId());
userDetail.setName(userData.getUserName());
List<MySimpleGrantedAuthority> roles = new ArrayList<>();
if (userData.getUserResources() != null) {
roles = userData.getUserResources().stream()
.map(Resource::getResourceCode)
.map(MySimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
MyAuthentication myAuthentication = new MyAuthentication(userDetail, roles);
SecurityContextHolder.getContext().setAuthentication(myAuthentication);
filterChain.doFilter(request, response);
}
}
}
}
github