前面介绍了很多基础设置的搭建方法,现在在前面的基础上,我们利用shiro来实现简单的登录及权限控制(只是简单的shiro集成,更细颗粒的shiro权限控制方法可以自己去学习shiro相关功能实现)。
框架基础:前面搭建的基础框架SpringBoot+JPA+MySQL
第一步:maven引入Jar包
<!-- shiro权限控制框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
第二步:前端登录页设计及方法
登录页代码就不贴了,自己弄个简单的登录页测试一下就行
第三步:后端设计
Shiro配置类:
package com.example.demo.shiro;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created by John.Zhang on 2017/10/10.
*/
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/");
// 未授权界面;
// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
// <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
System.out.println("Shiro拦截器工厂类注入成功");
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* 身份认证realm;
*
* @return
*/
@Bean
public ShiroRealm myShiroRealm() {
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(credentialsMatcher());
return myShiroRealm;
}
/**
* 凭证匹配器(用于加密密码使用,如不需要可以去除相关信息)
*/
@Bean
public CredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("MD5");
credentialsMatcher.setHashIterations(2);
return credentialsMatcher;
}
}
自定义认证:
package com.example.demo.shiro;
import com.example.demo.entities.Permission;
import com.example.demo.entities.Role;
import com.example.demo.entities.User;
import com.example.demo.service.UserSevice;
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.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
public class ShiroRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(ShiroRealm.class);
@Autowired
private UserSevice userSevice;
/**
* 登录认证
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
logger.info("验证当前Subject时获取到token为:" + token.toString());
//查出是否有此用户
User hasUser = userSevice.findByName(token.getUsername());
if (hasUser != null) {
if(hasUser.getIsLocked() != null && hasUser.getIsLocked().equals(User.IsLocked.LOCKED))
{
throw new LockedAccountException("用户已被锁定,无法登录");
}
List<Role> rlist = userSevice.findRolesByUid(hasUser.getId());//获取用户角色
List<Permission> plist = userSevice.findPermissionsByUid(hasUser.getId());//获取用户权限
List<String> roles = new ArrayList<>();
List<String> permissions = new ArrayList<>();
for(Role role : rlist){
roles.add(role.getName());
}
for(Permission permission : plist){
permissions.add(permission.getName());
}
hasUser.setRoleList(roles);
hasUser.setPermissionList(permissions);
// Session session = SecurityUtils.getSubject().getSession();
// session.setAttribute("user", hasUser);//成功则放入session
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
return new SimpleAuthenticationInfo(hasUser, hasUser.getPassword(),ByteSource.Util.bytes(hasUser.getSalt()), getName());
}else {
throw new UnknownAccountException("用户不存在");
}
}
/**
* 权限认证
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("##################执行Shiro权限认证##################");
User user = (User) principalCollection.getPrimaryPrincipal();
// //到数据库查是否有此对象
// User user = null;// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
// user = userMapper.findByName(loginName);
if (user != null) {
//权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//用户的角色集合
info.addRoles(user.getRoleList());
//用户的权限集合
info.addStringPermissions(user.getPermissionList());
return info;
}
// 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
return null;
}
}
用户实体类User:
package com.example.demo.entities;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import static javax.persistence.GenerationType.TABLE;
import static javax.persistence.TemporalType.TIMESTAMP;
@Entity
@Table(name = "tab_user")
public class User extends CommEntity{
@Id
@Column(name = "user_id")
@GeneratedValue(strategy = TABLE, generator = "sequence_generator")
@GenericGenerator(
name = "sequence_generator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@org.hibernate.annotations.Parameter(name = "segment_value", value = "user"),
@org.hibernate.annotations.Parameter(name = "increment_size", value = "100"),
@org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled")
})
private Long id;
@Column(name = "user_name",nullable = false,unique = true)
private String name;
@Column(name = "password",nullable = false)
private String password;
@Column(name = "user_salt",nullable = false)
private String salt = UUID.randomUUID().toString();
@Column(name = "user_status")
private Integer status = 1;
@Column(name = "is_locked")
private IsLocked isLocked = IsLocked.LOCKED;
@Transient
private List<String> roleList;
@Transient
private List<String> permissionList;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public List<String> getRoleList() {
return roleList;
}
public void setRoleList(List<String> roleList) {
this.roleList = roleList;
}
public List<String> getPermissionList() {
return permissionList;
}
public void setPermissionList(List<String> permissionList) {
this.permissionList = permissionList;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public IsLocked getIsLocked() {
return isLocked;
}
public void setIsLocked(IsLocked isLocked) {
this.isLocked = isLocked;
}
public static enum IsLocked{
LOCKED("锁定"),UNLOCKED("正常");
IsLocked(String type){
this.type = type;
}
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public static IsLocked getValue(Integer i){
switch (i){
case 0:
return LOCKED;
case 1:
return UNLOCKED;
default:
return null;
}
}
}
}
角色实体类Role:
package com.example.demo.entities;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
import static javax.persistence.GenerationType.TABLE;
import static javax.persistence.TemporalType.TIMESTAMP;
/**
* Created by John.Zhang on 2017/10/11.
*/
@Entity
@Table(name = "tab_sys_role")
public class Role extends CommEntity{
@Id
@Column(name = "role_id")
@GeneratedValue(strategy = TABLE, generator = "sequence_generator")
@GenericGenerator(
name = "sequence_generator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@org.hibernate.annotations.Parameter(name = "segment_value", value = "role"),
@org.hibernate.annotations.Parameter(name = "increment_size", value = "1"),
@org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled")
})
private Long id;
@Column(name = "role_name",unique = true,nullable = false)
private String name;
@Column(name = "role_type",nullable = false)
private Short type;
@Transient
private List<Permission> permissionList;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getType() {
return type;
}
public void setType(Short type) {
this.type = type;
}
public List<Permission> getPermissionList() {
return permissionList;
}
public void setPermissionList(List<Permission> permissionList) {
this.permissionList = permissionList;
}
}
用户角色映射类UserRole:
package com.example.demo.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import static javax.persistence.GenerationType.TABLE;
/**
* Created by John.Zhang on 2017/10/11.
*/
@Entity
@Table(name = "tab_user_role")
public class UserRole extends CommEntity{
@Id
@Column(name = "user_role_id")
@GeneratedValue(strategy = TABLE, generator = "sequence_generator")
@GenericGenerator(
name = "sequence_generator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@org.hibernate.annotations.Parameter(name = "segment_value", value = "user_role"),
@org.hibernate.annotations.Parameter(name = "increment_size", value = "1"),
@org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled")
})
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id")
private Role role;
@Column(name = "note")
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
权限基础类Permission:
package com.example.demo.entities;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.*;
import java.util.Date;
import static javax.persistence.GenerationType.TABLE;
import static javax.persistence.TemporalType.TIMESTAMP;
/**
* Created by John.Zhang on 2017/10/11.
*/
@Entity
@Table(name = "tab_sys_permission")
public class Permission extends CommEntity{
@Id
@Column(name = "permission_id")
@GeneratedValue(strategy = TABLE, generator = "sequence_generator")
@GenericGenerator(
name = "sequence_generator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@org.hibernate.annotations.Parameter(name = "segment_value", value = "permission"),
@org.hibernate.annotations.Parameter(name = "increment_size", value = "1"),
@org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled")
})
private Long id;
@Column(name = "permission_name",unique = true,nullable = false)
private String name;
@Column(name = "permission_url")
private String url;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
角色权限映射类RolePermission:
package com.example.demo.entities;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import static javax.persistence.GenerationType.TABLE;
/**
* Created by John.Zhang on 2017/10/11.
*/
@Entity
@Table(name = "tab_role_permission")
public class RolePermission extends CommEntity{
@Id
@Column(name = "role_permission_id")
@GeneratedValue(strategy = TABLE, generator = "sequence_generator")
@GenericGenerator(
name = "sequence_generator",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@org.hibernate.annotations.Parameter(name = "segment_value", value = "role_permission"),
@org.hibernate.annotations.Parameter(name = "increment_size", value = "1"),
@org.hibernate.annotations.Parameter(name = "optimizer", value = "pooled")
})
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id")
private Role role;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "permission_id")
private Permission permission;
@Column(name = "note")
private String note;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public Permission getPermission() {
return permission;
}
public void setPermission(Permission permission) {
this.permission = permission;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
公用实体类CommEntity:
package com.example.demo.entities;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import javax.persistence.Column;
import javax.persistence.Temporal;
import java.util.Date;
import static javax.persistence.TemporalType.TIMESTAMP;
/**
* Created by John.Zhang on 2017/10/11.
*/
public class CommEntity {
@CreatedDate
@Temporal(TIMESTAMP)
@Column(name = "create_time")
private Date createTime = new Date();
@LastModifiedDate
@Temporal(TIMESTAMP)
@Column(name = "modified_date")
private Date modifiedDate;
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getModifiedDate() {
return modifiedDate;
}
public void setModifiedDate(Date modifiedDate) {
this.modifiedDate = modifiedDate;
}
}
登录Controller :
package com.example.demo.controller;
import com.example.demo.entities.User;
import com.example.demo.entities.UserToken;
import com.example.demo.service.UserSevice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
/**
* Created by John.Zhang on 2017/10/9.
*/
@RestController
public class LoginController {
@Autowired
private UserSevice userSevice;
@PostMapping(value = "login")
public HashMap<String,Object> login(@RequestBody UserToken userToken){
return userSevice.login(userToken);
}
@PostMapping(value = "getLoginUser")
public User getLoginUser(){
return userSevice.getLoginUser();
}
}
路径跳转Controller :
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by John.Zhang on 2017/10/9.
*/
@Controller
public class PathController {
private String LoginPath = "Login";
private String IndexPath = "index";
@RequestMapping(value = "/")
public String loginInit(){
return LoginPath;
}
@RequestMapping(value = "index")
public String toIndex(){
return IndexPath;
}
}
方法实现类(接口类不贴了,自动生成就行):
package com.example.demo.service.impl;
import com.example.demo.entities.*;
import com.example.demo.repository.*;
import com.example.demo.service.UserSevice;
import com.example.demo.utils.DataTransUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Created by John.Zhang on 2017/10/9.
*/
@Service
public class UserSeviceImpl implements UserSevice{
@Autowired
private UserRepository userRepository;
@Autowired
private UserRoleRepository userRoleRepository;
@Autowired
private RolePermissionRepository rolePermissionRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PermissionRepository permissionRepository;
@Override
public HashMap<String, Object> login(UserToken userToken) {
UsernamePasswordToken token = new UsernamePasswordToken(userToken.getUsername(), userToken.getPassword());
try {
SecurityUtils.getSubject().login(token);
}catch (UnknownAccountException e){
return DataTransUtil.returnData(0,"用户名不存在");
}catch (IncorrectCredentialsException e) {
return DataTransUtil.returnData(0, "密码错误");
}catch (LockedAccountException e){
return DataTransUtil.returnData(0,"用户已被锁定,无法登录");
}catch (Exception e){
return DataTransUtil.returnData(0,"其他错误");
}
return DataTransUtil.returnData();
}
@Override
public User findByName(String username) {
return userRepository.findByName(username);
}
@Override
public User getLoginUser() {
return (User)SecurityUtils.getSubject().getPrincipal();
}
@Override
public List<Role> findRolesByUid(Long id) {
List<UserRole> userRoleList = userRoleRepository.findRolesByUid(id);
List<Role> roleList = new ArrayList<>();
for(UserRole userRole : userRoleList){
roleList.add(userRole.getRole());
}
return roleList;
}
@Override
public List<Permission> findPermissionsByUid(Long id) {
List<UserRole> userRoleList = userRoleRepository.findRolesByUid(id);
List<Long> roleIds = new ArrayList<>();
for(UserRole userRole : userRoleList){
roleIds.add(userRole.getRole().getId());
}
List<RolePermission> rolePermissionList = rolePermissionRepository.findPermissionsByUid(roleIds);
List<Permission> permissionList = new ArrayList<>();
for(RolePermission rolePermission : rolePermissionList){
if(!permissionList.contains(rolePermission.getPermission())){
permissionList.add(rolePermission.getPermission());
}
}
return permissionList;
}
}
Dao层(详细代码不贴了,自己定义就行):
工具类:
package com.example.demo.utils;
import org.springframework.util.StringUtils;
import java.util.HashMap;
/**
* Created by lean on 2017/9/18.
*/
public class DataTransUtil {
private static Integer defaultCode = 1;
private static String defaultMessage = "succesful";
public static HashMap<String,Object> returnData(Integer code,String message,Object data){
HashMap<String,Object> map = new HashMap<>();
if(null != code){
map.put("code",code);
}else{
map.put("code",defaultCode);
}
if(!StringUtils.isEmpty(message)){
map.put("message",message);
}else{
map.put("message",defaultMessage);
}
map.put("data",data);
return map;
}
public static HashMap<String,Object> returnData(Object data){
return returnData(null,null,data);
}
public static HashMap<String,Object> returnData(){
return returnData(null,null,null);
}
public static HashMap<String,Object> returnData(Integer code,String message){
return returnData(code,message,null);
}
}
从 getLoginUser 接口方法获得登录用户信息展示:
附加-密码加密方法:
package com.example.demo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
/**
* Created by John.Zhang on 2017/10/11.
*/
public class Test {
public static void main(String[] args) {
String newPassword = new SimpleHash(
"MD5", //加密算法
"123", //密码
ByteSource.Util.bytes("10086"), //salt盐 username + salt
2 //迭代次数
).toHex();
System.out.print(newPassword);
}
}