在前面的十六章我们讲解了如何构建我们的注册中心、链路监控中心、鉴权中心、权限架构生产者、权限架构消费者、路由网关,基于我们前面十六章构建的程序,从本章开始我们将具体的讲解如何搭建我们的权限架构系统的展示层。
首先在我们的工程中创建权限架构展示出的modules如下所示:
接着打开我们的pom.xml引入我们的maven依赖内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rbac</groupId>
<artifactId>rbac-show</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>rbac-show</name>
<description>权限架构展示层</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>com.base</groupId>
<artifactId>model</artifactId>
<version>[0.0.1-SNAPSHOT,)</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 开启spring-security的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 开启thymeleaf的spring-security的支持 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!-- 表示对thymeleaf模板不再是用默认的HTML5标准来做严格限制 -->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<!-- 添加对spring-redis的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖myboot项目的项目如果想要使用devtools,需要重新引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- 引入mybatis的支持 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 引入mapstruct的支持 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- Java EE 6 规范 JSR 330 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 添加对thymeleaf的支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 增加mapstruct自动编译实现生成impkl文件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
理论上我们所有的页面跳转都可以放到我们的API网关,但是基于当前的系统考虑,我还是把我的页面跳转放到当前单独的工程中,因此在该工程我们首先要实现对页面的权限控制,在当前工程中我使用了spring security来实现权限贡献,接下来我将讲解如何在我们当前的工程中集成spring security。
首先修改我们的工程底下的application.properties配置文件内容如下:
spring.profiles.active=dev
#配置放行的目录和方法
security.ignored=/api/*,/css/*,/js/*,/images/*,/fonts/*,/font-awesome/*
#表示对thymeleaf模板不再是用默认的HTML5标准来做严格限制
spring.thymeleaf.mode = LEGACYHTML5
#配置mybatis的扫描的包的文件的入口
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
接着在resource底下创建application-dev.properties配置文件内容如下:
server.port = 8080
#数据库连接配置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/hyll_springboot?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#连接池的配置信息
#初始化连接数
spring.datasource.initialSize=5
#最小空闲连接数
spring.datasource.minIdle=5
#最大连接数
spring.datasource.maxActive=20
#
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.jackson.serialization.indent_output=true
spring.redis.host=192.168.248.128
spring.redis.port=6379
spring.redis.pool.max-idle=20
#配置thymeleaf不做任何缓存
spring.thymeleaf.cache=false
接着在该工程底下新建如下的包结构:
接下来给大家详细讲解下每个包所需要存放的类所需要实现的功能:
config:用于存放配置文件的目录
base:存放通用工具实现类。
mybatis:mybatis配置文件,实现扫描mybatis文件。
security:实现集成spring security。
mvc:控制页面跳转。
util:工具类的包。
user:对用户操作的工具包。
uuid:获取uuid的工具包。
sys:系统文件夹。
controller:实现相应功能模块的controller的包。
dao:实现相应功能模块的dao的包。
entity:实体包。
mapper:实现实体之间的想换转换。
resource/mybatis:存放mybatis相应的xml文件的包。
mapper:存放相应xml的具体文件夹。
resource/static:存放我们的css、js、image等文件的包。
resource/templates:存放我们的thymeleaf文件的文件夹。
通过上面的介绍大家已经大概了解了每一个包的作用,那么我们现在开始集成我们的spring security,首先我们在entity底下创建User.java文件内容如下:
package com.rbac.rbacshow.sys.entity;
import com.base.entity.UserRole;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
*@author linzf
**/
public class User implements UserDetails {
private static final long serialVersionUID = -880218717499238863L;
public User(){
super();
}
public User(int id){
this.id = id;
}
private int id;
private String login;
private String password;
private String userName;
private String address;
private String job;
private long groupId;
private Date birthDate;
private String city;
private String district;
private String province;
private String streetAddress;
private String state;
private String type;
private Date lastLoginDate;
// 用户角色信息
private List<UserRole> roles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
if(this.getRoles()!=null){
List<UserRole> roles=this.getRoles();
for(UserRole role:roles){
if(role.getName()!=null){
auths.add(new SimpleGrantedAuthority(role.getName()));
}
}
}
return auths;
}
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return this.getLogin();
}
@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<UserRole> getRoles() {
return roles;
}
public void setRoles(List<UserRole> roles) {
this.roles = roles;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district = district;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getStreetAddress() {
return streetAddress;
}
public void setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
/**
* 功能描述:组装角色数据集合
* @param roleArray
*/
public void packagingRoles(String roleArray){
List<UserRole> roles = new ArrayList<UserRole>();
if(roleArray!=null){
UserRole userRole = null;
for(String roleId:roleArray.split(",")){
if(!roleId.isEmpty()){
userRole = new UserRole();
userRole.setId(Long.parseLong(roleId));
roles.add(userRole);
}
}
}
this.setRoles(roles);
}
}
接着在我们的dao底下创建UserDao.java文件内容如下:
package com.rbac.rbacshow.sys.dao;
import com.rbac.rbacshow.sys.entity.User;
/**
* Created by Administrator on 2018/1/24 0024.
*/
public interface UserDao {
/**
* 功能描述:根据账号来获取用户信息
* @param login
* @return
*/
User findByLogin(String login);
}
接着在我们的mybatis/mapper底下创建mybatis_user.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rbac.rbacshow.sys.dao.UserDao">
<!-- 包含角色信息的map -->
<resultMap type="com.rbac.rbacshow.sys.entity.User" id="UserLoginMap">
<id property="id" column="id"/>
<result property="login" column="login"/>
<result property="password" column="password"/>
<result property="userName" column="user_name"/>
<result property="address" column="address"/>
<result property="job" column="job"/>
<result property="groupId" column="group_id"/>
<result property="birthDate" column="birth_date"/>
<result property="city" column="city"/>
<result property="district" column="district"/>
<result property="province" column="province"/>
<result property="streetAddress" column="street_address"/>
<result property="state" column="state"/>
<result property="type" column="type"/>
<result property="lastLoginDate" column="last_login_date"/>
<collection property="roles" ofType="com.base.entity.UserRole" javaType="java.util.ArrayList">
<result column="user_role_id" property="id" jdbcType="VARCHAR" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="role_name" property="roleName" jdbcType="VARCHAR" />
</collection>
</resultMap>
<!-- 根据账号来获取用户信息 -->
<select id="findByLogin" parameterType="java.lang.String" resultMap="UserLoginMap">
select u.*,ur.id as user_role_id,ur.name,ur.role_name from user u inner join user_associate_role uar on u.id = uar.user_id inner join user_role ur on uar.role_id = ur.id where u.login = #{login}
</select>
</mapper>
接着在我们的mybatis包底下创建MyBatisConfig.java文件内容如下:
package com.rbac.rbacshow.common.config.mybatis;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/*
* 类描述:开启mybatis的支持
* @auther linzf
* @create 2018/02/25 0025
*/
@Configuration
@MapperScan("com.rbac.rbacshow.*.dao")
public class MyBatisConfig {
}
接着在我们的mapper创建UserMapper.java实现用户的相互转换,只所以要在此处实现用户之间的转换是引文spring security实现的User类和我们在基础工程中实现的User类是不同的,因此在我们将用户信息放到redis的时候我们需要将当前的用户信息转换为其他微服务可读的接口因此我们才需要在此处做一个转换,该类的内容如下:
package com.rbac.rbacshow.sys.mapper;
import com.rbac.rbacshow.sys.entity.User;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* Created by Administrator on 2018/2/2 0002.
*/
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(source = "id", target = "id")
com.base.entity.User userToBase(User user);
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
}
接着在我们的uuid包底下新建Uuid.java文件内容如下:
package com.rbac.rbacshow.common.util.uuid;
import java.util.Random;
import java.util.UUID;
/*
* 类描述:生成
* @auther linzf
* @create 2018/1/24 0024
*/
public class Uuid {
//token --------uuid
public static String getUUid(){
String token = UUID.randomUUID().toString().replaceAll("-", "");
return token;
}
//六位随机数
public static String getRandomNum(int pwd_len){
//35是因为数组是从0开始的,26个字母+10个数字
final int maxNum = 36;
int i; //生成的随机数
int count = 0; //生成的密码的长度
char[] str = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
StringBuffer pwd = new StringBuffer("");
Random r = new Random();
while(count < pwd_len){
//生成随机数,取绝对值,防止生成负数,
i = Math.abs(r.nextInt(maxNum)); //生成的数最大为36-1
if (i >= 0 && i < str.length) {
pwd.append(str[i]);
count ++;
}
}
return pwd.toString();
}
}
接着在我们的user包底下新建UserInfoUtil.java文件内容如下:
package com.rbac.rbacshow.common.util.user;
import com.base.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/*
* 类描述:用户操作类
* @auther linzf
* @create 2018/1/24 0024
*/
public class UserInfoUtil {
/**
* 功能描述:获取当前登陆的用户的信息
* 注释:强转一个null对象不会产生报错
* @return
*/
public static User getUser(){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
SecurityContextImpl securityContextImpl = (SecurityContextImpl) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
return (User) Optional.ofNullable(securityContextImpl.getAuthentication().getPrincipal()).orElse(null);
}
/**
* 功能描述:获取request对象
* @return
*/
public static HttpServletRequest getRequest(){
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
/**
* 功能描述:获取当前登陆的用户的权限集合
* @return
*/
public static List<GrantedAuthority> getGrantedAuthority(){
return (List<GrantedAuthority>)Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getAuthorities()).orElse(new ArrayList<>());
}
/**
* 功能描述:判断当前的用户是否包含某一个权限
* @param authority
* 注释:
* allMatch:Stream 中全部元素符合传入的 predicate,返回 true
* anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
* noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
* @return
*/
public static boolean hasAuthority(String authority){
return getGrantedAuthority().stream().anyMatch(obj->obj.getAuthority().equalsIgnoreCase(authority));
}
}
接着我们在我们的security包底下分别创建CustomPasswordEncoder.java(实现密码的MD5盐值加密)、LoginSuccessHandle.java
(实现不同的权限的页面跳转)、CustomUserService.java(实现重写登陆的判断逻辑)、WebSecurityConfig.java(实现spring security的配置),这四个文件的内容如下:
package com.rbac.rbacshow.common.config.security;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* spring-security登陆的密码进行MD5加密传到数据库
*/
public class CustomPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder.encodePassword(rawPassword.toString(), "hyll");
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
Md5PasswordEncoder encoder = new Md5PasswordEncoder();
return encoder.isPasswordValid(encodedPassword, rawPassword.toString(), "hyll");
}
}
package com.rbac.rbacshow.common.config.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
/*
* 类描述:实现根据不同的权限实现登陆的时候页面的跳转
* @auther linzf
* @create 2017/11/13 0013
*/
public class LoginSuccessHandle implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
String path = request.getContextPath() ;
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
// if (roles.contains("ROLE_DINER")){
// response.sendRedirect(basePath+"diningTable");
// return;
// }
response.sendRedirect(basePath+"main");
}
}
package com.rbac.rbacshow.common.config.security;
import com.base.util.ip.IPUtil;
import com.base.util.redis.RedisCache;
import com.rbac.rbacshow.common.util.user.UserInfoUtil;
import com.rbac.rbacshow.common.util.uuid.Uuid;
import com.rbac.rbacshow.sys.dao.UserDao;
import com.rbac.rbacshow.sys.entity.User;
import com.rbac.rbacshow.sys.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.inject.Inject;
import java.util.Date;
/**
* Created by Administrator on 2017/8/4 0004.
*/
public class CustomUserService implements UserDetailsService {
@Inject
private UserDao userDao;
@Autowired
private RedisCache redisCache;
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userDao.findByLogin(s);
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
// 生成当前登陆用户的token
String token = Uuid.getUUid();
// 将用户信息使用token保存到redis中并在一个小时以后过期
redisCache.setObject(token, userMapper.userToBase(user));
// 设置token的过期时间为3600秒
redisCache.expire(token,3600);
// 将生成的token重新放回页面
UserInfoUtil.getRequest().getSession().setAttribute("token",token);
// 获取当前登陆用户的真实IP地址
String IP = IPUtil.getIpAddress(UserInfoUtil.getRequest());
// 根据IP和token设置当前登陆的用户
redisCache.setObject(IP+"-"+token, userMapper.userToBase(user));
// 设置token的过期时间为3600秒
redisCache.expire(IP+"-"+token,3600);
// 用户登陆以后更新用户的最迟登陆时间
user.setLastLoginDate(new Date());
// 自定义错误的文章说明的地址:http://blog.csdn.net/z69183787/article/details/21190639?locationNum=1&fps=1
if(user.getState().equalsIgnoreCase("0")){
throw new LockedException("用户账号被冻结,无法登陆请联系管理员!");
}
return user;
}
}
package com.rbac.rbacshow.common.config.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
/**
* 实现Security的配置
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
UserDetailsService customUserService(){
return new CustomUserService();
}
@Bean
PasswordEncoder passwordEncoder(){
return new CustomPasswordEncoder();
}
@Bean
LoginSuccessHandle loginSuccessHandle(){return new LoginSuccessHandle();}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()).passwordEncoder(passwordEncoder());
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
/**
* 描述:csrf().disable()为了关闭跨域访问的限制,若不关闭则websocket无法与后台进行连接
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main")
.successHandler(loginSuccessHandle())
.failureUrl("/login?error=true")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login").
permitAll();
}
}
接着在mvc包底下创建WebMvcConfig.java文件内容如下:
package com.rbac.rbacshow.common.config.mvc;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/*
* 类描述:springMVC的配置
* @auther linzf
* @create 2018/1/24 0024
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
/**
* 重写方法描述:实现在url中输入相应的地址的时候直接跳转到某个地址
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 跳转到登陆页
registry.addViewController("/login").setViewName("login");
// 登陆成功跳转到首页
registry.addViewController("/main").setViewName("main");
// 登陆成功tab首页
registry.addViewController("/home").setViewName("home");
}
}
接着在我们的主入口类中注入redis,主入口类修改完成以后内容如下:
package com.rbac.rbacshow;
import com.base.util.redis.RedisCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class RbacShowApplication {
public static void main(String[] args) {
SpringApplication.run(RbacShowApplication.class, args);
}
@Bean
public RedisCache redisCache(){
return new RedisCache();
}
}
最后我们在static包底下引入相应的js、css以及image文件,在我们的templates底下创建login.html文件,内容如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<!--/
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Spring Security演示</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>
</nav>
-->
<div class="container">
<div class="starter-template">
<p th:if="${param.logout}" class="bg-warning">已成功注销</p><!-- 1 -->
<p th:if="${param.error}" th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}=='Bad credentials'?'账号/密码错误!':${session.SPRING_SECURITY_LAST_EXCEPTION.message}" class="bg-danger">
</p> <!-- 2 -->
<h2>使用账号密码登录</h2>
<form name="form" th:action="@{/login}" action="/login" method="POST"> <!-- 3 -->
<div class="form-group">
<label for="username">账号</label>
<input type="text" class="form-control" name="username" id="username" value="" placeholder="账号" />
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" name="password" id="password" placeholder="密码" />
</div>
<input type="submit" id="login" value="Login" class="btn btn-primary" />
</form>
</div>
</div>
</body>
</html>
main.html内容如下:
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head th:include="include/includebase"></head>
<link th:href="@{css/sb-admin.css}" rel="stylesheet"/>
<script th:inline="javascript">
$(function () {
// 初始化nav
$.fn.bootstrapNav({index:'main',navTitle:'XXXX管理系统'});
// 初始化标签页
$("#tabContainer").tabs({
data: [{
id: '99999999',
text: '首页',
url: "home",
closeable: false
}],
showIndex: 0,
loadAll: false
});
$.fn.bootstrapTree({url:api_url+"tree/mainTree",treeId:'menu_tree',tabId:"tabContainer"});
$.fn.dictUtil({url:api_url+"dict/loadDict"});
});
</script>
<body >
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header" id="navbar_header">
</div>
<!-- Top Menu Items -->
<ul class="nav navbar-right top-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-envelope"></i> <b class="caret"></b></a>
<ul class="dropdown-menu message-dropdown">
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="" />
</span>
<div class="media-body">
<h5 class="media-heading"><strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="" />
</span>
<div class="media-body">
<h5 class="media-heading"><strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="" />
</span>
<div class="media-body">
<h5 class="media-heading"><strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-footer">
<a href="#">Read All New Messages</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user"></i> <font th:text="${#authentication.name}"></font> <b class="caret"></b></a>
<ul class="dropdown-menu">
<li>
<a href="#" ><i class="fa fa-fw fa-gear"></i> 修改密码 </a>
</li>
<li class="divider"></li>
<li>
<a href="/logout" ><i class="fa fa-fw fa-power-off"></i>退 出</a>
</li>
</ul>
</li>
</ul>
<!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav" id="menu_tree">
</ul>
</div>
<!-- /.navbar-collapse -->
</nav>
<div id="page-wrapper" style="border-radius:5px 5px 0 0;">
<div id="tabContainer"></div>
</div>
</div>
<!--
<div th:text="${#authentication.name}">
The value of the "name" property of the authentication object should appear here.
</div>
这是一个登陆成功以后的首页
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody id="greetings">
</tbody>
</table>
</div>
</div>
-->
</body>
</html>
home.html内容如下:
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head th:include="include/includebase"></head>
<head th:include="include/includeHook"></head>
<body>
首页
</body>
</html>
大家会发现里面在头部都引入了include包底下的文件,这两个文件就是统一的引入通用的css和js的统一文件,大家直接将该项目从GitHub下载下来就可以看到了,此处就不再累述了,到此为止我们就完成了权限架构系统的展示层的基础构建工作,此刻大家可以将我们的基础权限架构系统给启动起来,直接打开浏览器访问http://127.0.0.1:8080/login我们会看到如下页面则说明我们的已经完成了基础架构的构建了:
到此为止的GitHub项目地址:https://github.com/185594-5-27/spring-cloud-rbac/tree/master-base-show
上一篇文章地址:基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十六)【路由网关】
下一篇文章地址:基于springboot+redis+bootstrap+mysql开发一套属于自己的分布式springcloud云权限架构(十八)【权限架构系统(改造)】
QQ交流群:578746866