1. 程式人生 > >springboot-27-security(一)

springboot-27-security(一)

mapper logout names turn 其中 權限 xmlns int 啟動程序

spring security 使用眾多的攔截器實現權限控制的, 其核心有2個重要的概念: 認證(Authentication) 和授權 (Authorization)), 認證就是確認用戶可以訪問當前系統, 授權即確定用戶有相應的權限,

現在先大概過一遍整個流程,用戶登陸,會被AuthenticationProcessingFilter攔截,調用AuthenticationManager的實現,而且AuthenticationManager會調用ProviderManager來獲取用戶驗證信息(不同的Provider調用的服務不同,因為這些信息可以是在數據庫上,可以是在LDAP服務器上,可以是xml配置文件上等),如果驗證通過後會將用戶的權限信息封裝一個User放到spring的全局緩存SecurityContextHolder中,以備後面訪問資源時使用。 訪問資源(即授權管理),訪問url時,會通過AbstractSecurityInterceptor攔截器攔截,其中會調用FilterInvocationSecurityMetadataSource的方法來獲取被攔截url所需的全部權限,在調用授權管理器AccessDecisionManager,這個授權管理器會通過spring的全局緩存SecurityContextHolder獲取用戶的權限信息,還會獲取被攔截的url和被攔截url所需的全部權限,然後根據所配的策略(有:一票決定,一票否定,少數服從多數等),如果權限足夠,則返回,權限不夠則報錯並調用權限不足頁面 ( http://blog.csdn.net/u012367513/article/details/38866465) 本例的用戶和角色信息存儲在mysql, 使用mybatis進行查詢: http://www.cnblogs.com/wenbronk/p/7351975.html

1, 導入基礎數據, 調試mybatis

1) , 建表

/*
Navicat MySQL Data Transfer
Source Server         : 本地
Source Host           : localhost:3306
Source Database       : test
Target Server Type    : MYSQL
Date: 2017-8-14 22:17:33
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- 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; -- ---------------------------- -- 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; -- ---------------------------- -- Table structure for `sys_role_user` -- ---------------------------- DROP TABLE IF EXISTS `sys_role_user`; CREATE TABLE `sys_role_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主鍵id, `sys_user_id` INT(32) NOT NULL COMMENT user_id, `sys_role_id` INT(32) NOT NULL COMMENT role_id, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8; ALTER TABLE sys_role_user ADD CONSTRAINT sys_FK1 FOREIGN KEY(sys_user_id) REFERENCES sys_user(id); ALTER TABLE sys_role_user ADD CONSTRAINT role_FK2 FOREIGN KEY(sys_role_id) REFERENCES sys_role(id);

導入數據

insert into SYS_USER (id,username, password) values (1,vini, 123);
insert into SYS_USER (id,username, password) values (2,bronk, 123);

insert into SYS_ROLE(id,name) values(1,ROLE_ADMIN);
insert into SYS_ROLE(id,name) values(2,ROLE_USER);

insert into SYS_ROLE_USER(SYS_USER_ID,sys_role_id) values(1,1);
insert into SYS_ROLE_USER(SYS_USER_ID,sys_role_id) values(2,2);

在 application.yml中配置如下, 可以在啟動程序時自動執行

spring: 
  datasource:
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: root
#    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
  # 一下2行 schema: classpath:security.sql data: classpath:security
-data.sql

2), mapper映射

package com.wenbronk.security.mapper

import com.wenbronk.security.entity.SysUser

/**
 * Created by wenbronk on 2017/8/14.
 */
interface SysUserMapper {
    SysUser findByUserName(String username)
}

在 resources/mybatis/mapper中添加:

<?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.wenbronk.security.mapper.SysUserMapper">

    <resultMap id="sys_user_map" type="SysUser">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="password" column="password" />
        <collection property="roles" ofType="SysRole">
            <result column="name" property="name" />
        </collection>

    </resultMap>

    <select id="findByUserName" parameterType="string" resultMap="sys_user_map">
        select u.id, u.username, u.password, r.name
        from sys_user u
        LEFT JOIN sys_role_user s on u.id = s.sys_user_id
        LEFT JOIN sys_role r on r.id = s.sys_role_id
        WHERE username = #{username}
    </select>
</mapper>

在application.yml中配置

mybatis:
  config-location: classpath:mybatis/SqlMapConfig.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

在main方法上添加mapper掃描:

@SpringBootApplication
@MapperScan("com.wenbronk.security.mapper")
public class SecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class);
    }
}

3) , 測試mybatis

    @Test
    void test2() {
        def name = sysUserMapper.findByUserName(vini)
        println name
    }

2, security相關配置

1), CustomerUserService.groovy
package com.wenbronk.security.security.service

import com.wenbronk.security.entity.SysRole
import com.wenbronk.security.entity.SysUser
import com.wenbronk.security.mapper.SysUserMapper
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.User
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 javax.inject.Inject
/**
 * Created by wenbronk on 2017/8/15.
 */
@Service
class CustomUserService implements UserDetailsService {

    @Inject
    SysUserMapper sysUserMapper;

    @Override
    UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        def sysUser = sysUserMapper.findByUserName(s) as SysUser
        assert sysUser != null
        List<SimpleGrantedAuthority> authorities = new ArrayList<>()
        for(SysRole role : sysUser.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName()))
            println role.getName();
        }
        return new User(sysUser.getUsername(), sysUser.getPassword(), authorities)
    }
}

2), WebSecurityConfig.groovy

package com.wenbronk.security.security.config

import com.wenbronk.security.security.service.CustomUserService
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.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

import javax.inject.Inject
/**
 * Created by wenbronk on 2017/8/15.
 */
@Configuration
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Inject
    CustomUserService customUserService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest().authenticated()   // 任何請求都攔截
            .and()
            .formLogin()
            .loginPage("/login")
            .failureUrl("/login?error")
            .permitAll()        // 登陸後可訪問任意頁面
            .and()
            .logout().permitAll();  // 註銷後任意訪問

    }
}

3, 頁面

1), 頁面轉向設置

package com.wenbronk.security.config

import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
/**
 * Created by wenbronk on 2017/8/15.
 */
@Configuration
class WebMvcConfig extends WebMvcConfigurerAdapter{

    @Override
    void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login")
    }
}

controller.groovy

package com.wenbronk.security.controller

import com.wenbronk.security.entity.Msg
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.RequestMapping
/**
 * Created by wenbronk on 2017/8/14.
 */
@Controller
class SecurityController {

    @RequestMapping("/")
    def index(Model model) {
        def msg = new Msg("測試標題", "測試內容", "額外信息, 只對管理員顯示")
        model.addAttribute("msg", msg);
        "home"
    }
    
}

在resources下放入靜態資源, bootstramp.min.css, 以及thymeleaf頁面

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="@{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}" 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" 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="Login" class="btn btn-primary" />
        </form>
    </div>
</div>
</body>
</html>

home.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta content="text/html;charset=UTF-8"/>
<title sec:authentication="name"></title>
<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">
          <h1 th:text="${msg.title}"></h1>

        <p class="bg-primary" th:text="${msg.content}"></p>

        <div sec:authorize="hasRole(‘ROLE_ADMIN‘)"> <!-- 用戶類型為ROLE_ADMIN 顯示 -->
             <p class="bg-info" th:text="${msg.etraInfo}"></p>
        </div>

        <div sec:authorize="hasRole(‘ROLE_USER‘)"> <!-- 用戶類型為 ROLE_USER 顯示 -->
             <p class="bg-info">無更多信息顯示</p>
        </div>

        <form th:action="@{/logout}" method="post">
            <input type="submit" class="btn btn-primary" value="註銷"/>
        </form>
      </div>

    </div>
</body>


</html>

參見: JavaEE顛覆者, springboot實戰

springboot-27-security(一)