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 justfresh@foxmail.com
* @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 justfresh@foxmail.com
* @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 justfresh@foxmail.com
* @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账户(普通用户)即可进行鉴权访问。