《SpringCloud学习第二阶段》—— 整合Security

1、通过Security内置登录界面访问。

pom.xml添加spring-boot-starter-web、spring-boot-starter-test、spring-boot-starter-security依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.properties文件添加如下配置,用作配置Security登录用户名和密码。

# 配置Security账号密码
security.user.name=admin
security.user.password=123456

启动微服务并访问任何接口,即可发现会弹出如下图的Security权限校验界面,输入对应的用户名(admin)、密码(123456)登录即可。


2、自定义登录界面通过指定用户名密码访问。

pom引入如步骤1的依赖包及spring-boot-starter-thymeleaf依赖,application.properties文件中去除原有Security用户名和密码的配置。

在项目默认包(例如本项目启动类所在包为com.xiudoua.micro.study)结构下新建config包,在config包下新建SecurityConfig.java配置类文件,用作配置Security用户名、密码等,代码如下:

/**
 * Copyright (c) 2018,http://www.xiudoua.com. 
 * All Rights Reserved.
 */
package com.xiudoua.micro.study.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
 * @desc 配置SpringSecurity访问所需信息
 * @author JustFresh
 * @email [email protected]
 * @time 2018年6月21日 下午4:31:07
 * @version 1.0.0
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().withUser("admin").password("123456").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.formLogin().loginPage("/login").permitAll()
		.defaultSuccessUrl("/welcome").permitAll()
		.and().authorizeRequests()
		.anyRequest().authenticated()
		.antMatchers("/login.html", "/**/**.css", "/images/**", "**/**.js","/index").permitAll()//解决静态资源被拦截的问题
		.and().logout().permitAll();
	}

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/login.html", "/**/**.css", "/**/images/**", "**/**.js","/welcome");
	}
}
上面代码配置认证用户账号为:admin    密码:123456,登录页面为templates文件夹下login.html,登录成功跳转页面为templates目录下welcome.html文件;

在src/main/templates目录下引入bootstrap、jquery、layer等前端所需资源,结构如下图所示:


login.html登录页面代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
<link rel="stylesheet" th:href="@{assets/css/bootstrap.css}"/>
<style type="text/css">
body {padding-top: 50px;}
.starter-template {padding: 40px 15px;text-align: center;}
</style>
</head>
<body>
     <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="#">XIUDOUA</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
           <li><a th:href="@{/}"> 首页 </a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </div>
     <div class="container">
      <div class="starter-template">
       <p th:if="${param.logout}" class="bg-warning">已成功注销</p>
            <p th:if="${param.error}" class="bg-danger">有错误,请重试</p>
            <h2>使用账号密码登录</h2>
            <form name="form" th:action="@{/login}" action="/login" method="POST" id="loginForm">
                <div class="form-group">
                    <label for="username">账号</label>
                    <input type="text" class="form-control" name="username" value="" placeholder="账号" />
                </div>
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="password" class="form-control" name="password" placeholder="密码" />
                </div>
                <input type="submit" id="login" value="登 录" class="btn btn-primary" />
            </form>
      </div>
    </div>
    <script type="text/javascript" src="/assets/js/jquery.js"></script>
</body>
</html>

success.html页面内容如下:

<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录成功页面</title>
</head>
<body>
    登录成功
</body>
</html>

运行启动服务,访问http://localhost:8080,则会默认跳转到登录页面(http://localhost:8080/login),如下图所示:


账号输入admin,密码输入123456,点击登录即可跳转到登录成功页面。

3、通过读取数据库账号密码登录并进行角色鉴权。

所用到技术:SpringBoot下SSM+MySql+Bootstrap。

3.1.新建MySql表结构并导入表数据。

在Mysql新建名为security_test的数据库,并导入如下sql数据(导入成功后JustFresh账号拥有admin权限,ZhangSan账号拥有user权限):

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(32) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(32) DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'ROLE_ADMIN');
INSERT INTO `sys_role` VALUES ('2', 'ROLE_USER');

-- ----------------------------
-- Table structure for sys_role_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_user`;
CREATE TABLE `sys_role_user` (
  `sys_user_id` int(32) NOT NULL COMMENT 'user_id',
  `sys_role_id` int(32) NOT NULL COMMENT 'role_id',
  PRIMARY KEY (`sys_user_id`,`sys_role_id`),
  KEY `role_FK2` (`sys_role_id`),
  CONSTRAINT `role_FK2` FOREIGN KEY (`sys_role_id`) REFERENCES `sys_role` (`id`),
  CONSTRAINT `sys_FK1` FOREIGN KEY (`sys_user_id`) REFERENCES `sys_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role_user
-- ----------------------------
INSERT INTO `sys_role_user` VALUES ('1', '1');
INSERT INTO `sys_role_user` VALUES ('2', '2');

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(32) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `username` varchar(32) DEFAULT NULL COMMENT '用户名',
  `password` varchar(32) DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'JustFresh', '96e79218965eb72c92a549dd5a330112');
INSERT INTO `sys_user` VALUES ('2', 'ZhangSan', '96e79218965eb72c92a549dd5a330112');
3.2. 搭建项目结构。

pom文件引入spring-boot-starter-web、spring-boot-starter-test、spring-boot-starter-security、spring-boot-starter-thymeleaf、mysql-connector-java、mybatis-spring-boot-starter。

新建src/main/resources原文件夹,并添加application.properties配置文件,具体配置如下:

server.port=8090
spring.application.name=security-custom-bydb

#thymeleaf模板引擎引入
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML5
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html

#MySql数据库链接配置
spring.datasource.url=jdbc:mysql://localhost:3306/security_test?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

#MyBatis配置
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.xiudoua.micro.entity

新建如下图项目结构及配置:


其中generatorConfig.xml文件为MyBatis自动生成实体及Mapper的配置,详细配置如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
	<!-- classPathEntry:数据库的JDBC驱动的jar包地址 -->
	<classPathEntry location="D:\software\mysql-connector-java-5.1.21.jar" />
	<context id="context1">
		<commentGenerator>
			<!-- 是否去除自动生成的注释 true:是 : false:否 -->
			<property name="suppressAllComments" value="true" />
                        <property name="suppressDate" value="true" /> <!-- 是否生成注释代时间戳-->  
		</commentGenerator>

		<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
		<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/security_test" userId="root" password="root" />

		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
		<javaTypeResolver>
			<property name="forceBigDecimals" value="false" />
		</javaTypeResolver>

		<!-- targetProject:自动生成代码的位置,写自己的工程名字 -->
		<!-- 生成的mapper文件 -->
		<javaModelGenerator targetPackage="com.xiudoua.micro.entity" targetProject="security-custom-bydb/src/main/java">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="true" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>

		<!-- Model XML文件保存位置 -->
		<sqlMapGenerator targetPackage="mapper"
			targetProject="security-custom-bydb/src/main/resources">
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>

		<!-- 生成的查询条件的类 -->
		<javaClientGenerator targetPackage="com.xiudoua.micro.mapper"
			targetProject="security-custom-bydb/src/main/java" type="XMLMAPPER">
			<property name="enableSubPackages" value="true" />
		</javaClientGenerator>

 		<table schema="" tableName="sys_role" 
			domainObjectName="SysRole" enableCountByExample="true"
			enableUpdateByExample="true" enableDeleteByExample="true"
			enableSelectByExample="true" selectByExampleQueryId="true"> 
		</table>
		
		<table schema="" tableName="sys_role_user" 
			domainObjectName="SysRoleUser" enableCountByExample="true"
			enableUpdateByExample="true" enableDeleteByExample="true"
			enableSelectByExample="true" selectByExampleQueryId="true"> 
		</table>
		
		<table schema="" tableName="sys_user" 
			domainObjectName="SysUser" enableCountByExample="true"
			enableUpdateByExample="true" enableDeleteByExample="true"
			enableSelectByExample="true" selectByExampleQueryId="true"> 
		</table>
	</context>
</generatorConfiguration>

执行MyBatis自动生成,生成SysUser、SysRole、SysRoleUser对应的实体、example以及mapper;修改SysRole对象实现GrantedAuthority接口;改SysUser实现Security的UserDetails接口,给他SysUser对象添加private List<SysRole> roles;属性,用作用户角色一对多的对应,完整代码如下:

1.SysRole对象。

package com.xiudoua.micro.entity;
import org.springframework.security.core.GrantedAuthority;
public class SysRole  implements GrantedAuthority{

    private static final long serialVersionUID = -3957539165716897200L;
    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }
    @Override
    public String getAuthority() {
	return this.getName();
    }
}

1.SysUser对象。

package com.xiudoua.micro.entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class SysUser implements UserDetails{

    private static final long serialVersionUID = 1L;
    private Integer id;
    private String username;
    private String password;
    private List<SysRole> roles;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
	List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
        List<SysRole> roles = this.getRoles();
        for (SysRole role : roles) {
            auths.add(new SimpleGrantedAuthority(role.getAuthority()));
        }
        return auths;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

	public List<SysRole> getRoles() {
		return roles;
	}

	public void setRoles(List<SysRole> roles) {
		this.roles = roles;
	}
	
}

新建IUserService接口及UserServiceImpl实现类,添加一个loadByUsername的方法(通过传入用户名查询SysUser对象)。实现代码如下:

@Transactional
@Service(value = "userService")
public class UserServiceImpl implements IUserService{
	@Autowired
	private SysUserMapper userMapper;
	@Autowired
	private SysRoleMapper roleMapper;
	@Autowired
	private SysRoleUserMapper roleUserMapper;
	@Override
	public SysUser loadByUsername(String username) {
		SysUserExample example = new SysUserExample();
		example.createCriteria().andUsernameEqualTo(username);
		List<SysUser> userList = this.userMapper.selectByExample(example);
		if (userList != null && userList.size() > 0) {
			SysUser res = userList.get(0);
			// 查询用户-角色列表
			SysRoleUserExample userRoleExample = new SysRoleUserExample();
			userRoleExample.createCriteria().andSysUserIdEqualTo(userList.get(0).getId());
			List<SysRoleUserKey> userRoleList = this.roleUserMapper.selectByExample(userRoleExample);
			if (userRoleList != null && userRoleList.size() > 0) {
				List<Integer> idList = new ArrayList<Integer>();
				for (SysRoleUserKey userRoleKey : userRoleList) {
					idList.add(userRoleKey.getSysRoleId());
				}
				SysRoleExample roleExample = new SysRoleExample();
				roleExample.createCriteria().andIdIn(idList);
				List<SysRole> roleList = this.roleMapper.selectByExample(roleExample);
				res.setRoles(roleList);
				return res;
			}
		}
		return null;
	}
}

在com.xiudoua.micro.security包下新建CustomUserService.java,实现Security的UserDetailsService,具体代码如下:

/**
 * Copyright (c) 2018,http://www.xiudoua.com. 
 * All Rights Reserved.
 */
package com.xiudoua.micro.security;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.xiudoua.micro.entity.SysRole;
import com.xiudoua.micro.entity.SysUser;
import com.xiudoua.micro.service.IUserService;

/**
 * @desc Security操作登录的类
 * @author JustFresh
 * @email [email protected]
 * @time 2018年6月22日 下午3:48:32
 * @version 1.0.0
 */
@Service(value = "customUserService")
public class CustomUserService implements UserDetailsService {

	protected Logger logger = LoggerFactory.getLogger(CustomUserService.class);
	@Autowired
	private IUserService userService;
	@Override
	public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {
		try {
			SysUser user = userService.loadByUsername(username);
			if (user == null) {
	            throw new UsernameNotFoundException("用户名不存在!");
	        }
	        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
	        //用于添加用户的权限。只要把用户权限添加到authorities 就万事大吉。
	        for(SysRole role:user.getRoles()){
	            authorities.add(new SimpleGrantedAuthority(role.getAuthority()));
	            logger.info("loadUserByUsername: " + user);
	        }
	        return user;
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("loadUserByUsername失败:{}",e);
		}
        throw new UsernameNotFoundException("用户不存在");
	}
}
在com.xiudoua.micro.config包下新建SecurityConfig.java,实现Security的 UserDetailsService, 具体代码如下:
/**
 * Copyright (c) 2018,http://www.xiudoua.com. 
 * All Rights Reserved.
 */
package com.xiudoua.micro.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.xiudoua.micro.security.CustomUserService;
import com.xiudoua.micro.utils.MD5Util;
/**
 * @desc
 * @author JustFresh
 * @email [email protected]
 * @time 2018年6月22日 下午3:58:28
 * @version 1.0.0
 */
@Configuration
public class SecurityConfig  extends WebSecurityConfigurerAdapter{

    @Bean
    UserDetailsService customUserService() {
        return new CustomUserService();
    }

    /**
     * 配置密码加密方式(MD5)
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    	auth.userDetailsService(customUserService()).passwordEncoder(new PasswordEncoder(){
			public String encode(CharSequence rawPassword) {
				return MD5Util.string2MD5((String)rawPassword);
			}
			public boolean matches(CharSequence rawPassword, String encodedPassword) {
				return encodedPassword.equals(MD5Util.string2MD5((String)rawPassword));
			}
        });
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	
    	http.authorizeRequests()
    	.antMatchers("/login.html", "/**/**.css", "/images/**", "**/**.js","/index").permitAll()//解决静态资源被拦截的问题
        .antMatchers("/user/**").access("hasRole('USER')")
        .antMatchers("/admin/**").access("hasRole('ADMIN')")
        .and().formLogin().loginPage("/login").defaultSuccessUrl("/welcome").permitAll()
        .and().csrf()
        .and().exceptionHandling().accessDeniedPage("/denied");
    	http.csrf().disable();//禁用CSRF
    }
	
}
templates目录下分别新建login.html、welcome.html(默认登录成功跳转页面)、denied.html(权限拒绝跳转页面)、admin/index.html(管理员可以访问页面)、user/index.html(普通用户可访问页面)。启动项目,分别登录JustFresh账户(管理员)、ZhangSan账户(普通用户)即可进行鉴权访问。

猜你喜欢

转载自blog.csdn.net/u012459871/article/details/80761749
今日推荐